In this chapter, we will cover the following topics:
PHP 7 installation considerations
Using the built-in PHP web server
Defining a test MySQL database
Installing PHPUnit
Implementing class autoloading
Hoovering a website
Building a deep web scanner
Creating a PHP 5 to PHP 7 code converter
This chapter is designed as a quick start that will get you up and running on PHP 7 so that you can start implementing the recipes right away. The underlying assumption for this book is that you already have a good knowledge of PHP and programming. Although this book will not go into detail about the actual installation of PHP, given that PHP 7 is relatively new, we will do our best to point out the quirks and gotchas you might encounter during a PHP 7 installation.
There are three primary means of acquiring PHP 7:
Downloading and installing directly from the source code
Installing pre-compiled binaries
Installing a *AMP package (that is, XAMPP, WAMP, LAMP, MAMP, and so on)
The three methods are listed in order of difficulty. However, the first approach, although tedious, will give you the most finite control over extensions and options.
In order to utilize this approach, you will need to have a C compiler available. If you are running Windows, MinGW is a free compiler that has proven popular. It is based on the GNU Compiler Collection (GCC) compiler provided by the GNU project. Non-free compilers include the classic Turbo C compiler from Borland, and, of course, the compiler that is preferred by Windows developers is Visual Studio. The latter, however, is designed mainly for C++ development, so when you compile PHP, you will need to specify C mode.
When working on an Apple Mac, the best solution is to install the Apple Developer Tools. You can use the Xcode IDE to compile PHP 7, or run gcc
from a terminal window. In a Linux environment, from a terminal window, run gcc
.
When compiling from a terminal window or command line, the normal procedure is as follows:
configure
make
make test
make install
For information on configuration options (that is, when running configure
), use the help
option:
configure --help
Errors you might encounter during the configuration stage are mentioned in the following table:
Error |
Fix |
---|---|
|
You just need to install http://superuser.com/questions/740399/how-to-fix-php-installation-when-xml2-config-is-missing |
|
Install |
|
Not a big deal. These options are defaults and don't need to be included. For more details, please refer to the following link: http://jcutrer.com/howto/linux/how-to-compile-php7-on-ubuntu-14-04 |
As the title implies, pre-compiled binaries are a set of binary files that somebody else has kindly compiled from PHP 7 source code and has made available.
In the case of Windows, go to http://windows.php.net/. You will find a good set of tips in the left column that pertain to which version to choose, thread safe versus non-read safe, and so forth. You can then click on Downloads and look for the ZIP file that applies to your environment. Once the ZIP file has been downloaded, extract the files into the folder of your choice, add php.exe
to your path, and configure PHP 7 using the php.ini
file.
To install the pre-compiled binaries on a Mac OS X system, it is best to involve a package management system. The ones recommended for PHP include the following:
MacPorts
Liip
Fink
Homebrew
In the case of Linux, the packaging system used depends on which Linux distribution you are using. The following table, organized by Linux distribution, summarizes where to look for the PHP 7 package.
AMP refers to Apache, MySQL, and PHP (also Perl and Python). The * refers to Linux, Windows, Mac, and so on (that is, LAMP, WAMP, and MAMP). This approach is often the easiest, but gives you less control over the initial PHP installation. On the other hand, you can always modify the php.ini
file and install additional extensions to customize your installation as needed. The following table summarizes a number of popular *AMP packages:
Package |
Where is it found |
Free? |
Supports* |
---|---|---|---|
|
Y |
WML | |
|
Y |
WML | |
|
Y |
WM | |
|
Y |
W | |
|
Y |
W | |
|
N |
WML |
In the preceding table, we've enlisted the *AMP packages where * is replaced by W for Windows, M for Mac OS X, and L for Linux.
When you install a pre-compiled binary from a package, only core
extensions are installed. Non-core PHP extensions must be installed separately.
It's worth noting that PHP 7 installation on cloud computing platforms will often follow the installation procedure outlined for pre-compiled binaries. Find out if your cloud environment uses Linux, Mac, or Windows virtual machines, and then follow the appropriate procedure as mentioned in this recipe.
It's possible that PHP 7 hasn't yet reached your favorite repository for pre-compiled binaries. You can always install from source, or consider installing one of the *AMP packages (see the next section). An alternative for Linux-based systems is to use the Personal Package Archive (PPA) approach. Because PPAs have not undergone a rigorous screening process, however, security could be a concern. A good discussion on security considerations for PPAs is found at http://askubuntu.com/questions/35629/are-ppas-safe-to-add-to-my-system-and-what-are-some-red-flags-to-watch-out-fo.
General installation considerations, as well as instructions for each of the three major OS platforms (Windows, Mac OS X, and Linux), can be found at http://php.net/manual/en/install.general.php.
The website for MinGW is http://www.mingw.org/.
Instructions on how to compile a C program using Visual Studio can be found at https://msdn.microsoft.com/en-us/library/bb384838.
Another possible way to test PHP 7 is by using a virtual machine. Here are a couple of tools with their links, which might prove useful:
Vagrant: https://github.com/rlerdorf/php7dev (php7dev is a Debian 8 Vagrant image that is preconfigured for testing PHP apps and developing extensions across many versions of PHP)
Docker: https://hub.docker.com/r/coderstephen/php7/ (it contains a PHP7 Docker container)
Aside from unit testing and running PHP directly from the command line, the obvious way to test your applications is to use a web server. For long-term projects, it would be beneficial to develop a virtual host definition for a web server that most closely mirrors the one used by your customer. Creating such definitions for the various web servers (that is, Apache, NGINX, and so on) is beyond the scope of this book. Another quick and easy-to-use alternative (which we have room to discuss here) is to use the built-in PHP 7 web server.
To activate the PHP web server, first change to the directory that will serve as the base for your code.
You then need to supply the hostname or IP address and, optionally, a port. Here is an example you can use to run the recipes supplied with this book:
cd /path/to/recipes php -S localhost:8080
You will see output on your screen that looks something like this:
As the built-in web server continues to service requests, you will also see access information, HTTP status codes, and request information.
If you need to set the web server document root to a directory other than the current one, you can use the
-t
flag. The flag must then be followed by a valid directory path. The built-in web server will treat this directory as if it were the web document root, which is useful for security reasons. For security reasons, some frameworks, such as Zend Framework, require that the web document root is different from where your actual source code resides.Here is an example using the
-t
flag:php -S localhost:8080 -t source/chapter01
Here is an example of the output:
For test purposes, along with the source code for the book, we've provided an SQL file with sample data at https://github.com/dbierer/php7cookbook. The name of the database used in the recipes for this book is php7cookbook
.
Define a MySQL database,
php7cookbook
. Also assign rights to the new database to a user calledcook
with the passwordbook
. The following table summarizes these settings:Item
Notes
Database name
php7cookbook
Database user
cook
Database user password
book
Here is an example of SQL needed to create the database:
CREATE DATABASE IF NOT EXISTS dbname DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; CREATE USER 'user'@'%' IDENTIFIED WITH mysql_native_password; SET PASSWORD FOR 'user'@'%' = PASSWORD('userPassword'); GRANT ALL PRIVILEGES ON dbname.* to 'user'@'%'; GRANT ALL PRIVILEGES ON dbname.* to 'user'@'localhost'; FLUSH PRIVILEGES;
Import the sample values into the new database. The import file,
php7cookbook.sql
, is located at https://github.com/dbierer/php7cookbook/blob/master/php7cookbook.sql.
Unit testing is arguably the most popular means of testing PHP code. Most developers will agree that a solid suite of tests is a requirement for any properly developed project. Few developers actually write these tests. A lucky few have an independent testing group that writes the tests for them! After months of skirmishing with the testing group, however, the remains of the lucky few tend to grumble and complain. In any event, any book on PHP would not be complete without at least a nod and a wink towards testing.
The place to find the latest version of PHPUnit is https://phpunit.de/. PHPUnit5.1 and above support PHP 7. Click on the link for the desired version, and you will download a phpunit.phar
file. You can then execute commands using the archive, as follows:
php phpunit.phar <command>
When developing PHP using an object-oriented programming (OOP) approach, the recommendation is to place each class in its own file. The advantage of following this recommendation is the ease of long-term maintenance and improved readability. The disadvantage is that each class definition file must be included (that is, using include
or its variants). To address this issue, there is a mechanism built into the PHP language that will autoload any class that has not already been specifically included.
The minimum requirement for PHP autoloading is to define a global __autoload()
function. This is a magic function called automatically by the PHP engine when a class is requested but where said class has not been included. The name of the requested class will appear as a parameter when __autoload()
is invoked (assuming that you have defined it!). If you are using PHP namespaces, the full namespaced name of the class will be passed. Because __autoload()
is a function, it must be in the global namespace; however, there are limitations on its use. Accordingly, in this recipe, we will make use of the spl_autoload_register()
function, which gives us more flexibility.
The class we will cover in this recipe is
Application\Autoload\Loader
. In order to take advantage of the relationship between PHP namespaces and autoloading, we name the fileLoader.php
and place it in the/path/to/cookbook/files/Application/Autoload
folder.The first method we will present simply loads a file. We use
file_exists()
to check before runningrequire_once()
. The reason for this is that if the file is not found,require_once()
will generate a fatal error that cannot be caught using PHP 7's new error handling capabilities:protected static function loadFile($file) { if (file_exists($file)) { require_once $file; return TRUE; } return FALSE; }
We can then test the return value of
loadFile()
in the calling program and loop through a list of alternate directories before throwing anException
if it's ultimately unable to load the file.Next, we define the method that calls
loadFile()
and actually performs the logic to locate the file based on the namespaced classname. This method derives a filename by converting the PHP namespace separator\
into the directory separator appropriate for this server and appending.php
:public static function autoLoad($class) { $success = FALSE; $fn = str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php'; foreach (self::$dirs as $start) { $file = $start . DIRECTORY_SEPARATOR . $fn; if (self::loadFile($file)) { $success = TRUE; break; } } if (!$success) { if (!self::loadFile(__DIR__ . DIRECTORY_SEPARATOR . $fn)) { throw new \Exception( self::UNABLE_TO_LOAD . ' ' . $class); } } return $success; }
Next, the method loops through an array of directories we call
self::$dirs
, using each directory as a starting point for the derived filename. If not successful, as a last resort, the method attempts to load the file from the current directory. If even that is not successful, anException
is thrown.Next, we need a method that can add more directories to our list of directories to test. Notice that if the value provided is an array,
array_merge()
is used. Otherwise, we simply add the directory string to theself::$dirs
array:public static function addDirs($dirs) { if (is_array($dirs)) { self::$dirs = array_merge(self::$dirs, $dirs); } else { self::$dirs[] = $dirs; } }
Then, we come to the most important part; we need to register our
autoload()
method as a Standard PHP Library (SPL) autoloader. This is accomplished usingspl_autoload_register()
with theinit()
method:public static function init($dirs = array()) { if ($dirs) { self::addDirs($dirs); } if (self::$registered == 0) { spl_autoload_register(__CLASS__ . '::autoload'); self::$registered++; } }
At this point, we can define
__construct()
, which callsself::init($dirs)
. This allows us to also create an instance ofLoader
if desired:public function __construct($dirs = array()) { self::init($dirs); }
In order to use the autoloader class that we just defined, you will need to require Loader.php
. If your namespace files are located in a directory other than the current one, you should also run Loader::init()
and supply additional directory paths.
In order to make sure the autoloader works, we'll also need a test class. Here is a definition of /path/to/cookbook/files/Application/Test/TestClass.php
:
<?php namespace Application\Test; class TestClass { public function getTest() { return __METHOD__; } }
Now create a sample chap_01_autoload_test.php
code file to test the autoloader:
<?php require __DIR__ . '/../Application/Autoload/Loader.php'; Application\Autoload\Loader::init(__DIR__ . '/..');
Next, get an instance of a class that has not already been loaded:
$test = new Application\Test\TestClass(); echo $test->getTest();
Finally, try to get a fake
class that does not exist. Note that this will throw an error:
$fake = new Application\Test\FakeClass(); echo $fake->getTest();
Very frequently, it is of interest to scan a website and extract information from specific tags. This basic mechanism can be used to trawl the web in search of useful bits of information. At other times you need to get a list of <IMG>
tags and the SRC
attribute, or <A>
tags and the corresponding HREF
attribute. The possibilities are endless.
First of all, we need to grab the contents of the target website. At first glance it seems that we should make a cURL request, or simply use
file_get_contents()
. The problem with these approaches is that we will end up having to do a massive amount of string manipulation, most likely having to make inordinate use of the dreaded regular expression. In order to avoid all of this, we'll simply take advantage of an already existing PHP 7 classDOMDocument
. So we create aDOMDocument
instance, setting it to UTF-8. We don't care about whitespace, and use the handyloadHTMLFile()
method to load the contents of the website into the object:public function getContent($url) { if (!$this->content) { if (stripos($url, 'http') !== 0) { $url = 'http://' . $url; } $this->content = new DOMDocument('1.0', 'utf-8'); $this->content->preserveWhiteSpace = FALSE; // @ used to suppress warnings generated from // improperly configured web pages @$this->content->loadHTMLFile($url); } return $this->content; }
Tip
Note that we precede the call to the
loadHTMLFile()
method with an@
. This is not done to obscure bad coding (!
) as was often the case in PHP 5! Rather, the@
suppresses notices generated when the parser encounters poorly written HTML. Presumably, we could capture the notices and log them, possibly giving ourHoover
class a diagnostic capability as well.Next, we need to extract the tags which are of interest. We use the
getElementsByTagName()
method for this purpose. If we wish to extract all tags, we can supply*
as an argument:public function getTags($url, $tag) { $count = 0; $result = array(); $elements = $this->getContent($url) ->getElementsByTagName($tag); foreach ($elements as $node) { $result[$count]['value'] = trim(preg_replace('/\s+/', ' ', $node->nodeValue)); if ($node->hasAttributes()) { foreach ($node->attributes as $name => $attr) { $result[$count]['attributes'][$name] = $attr->value; } } $count++; } return $result; }
It might also be of interest to extract certain attributes rather than tags. Accordingly, we define another method for this purpose. In this case, we need to parse through all tags and use
getAttribute()
. You'll notice that there is a parameter for the DNS domain. We've added this in order to keep the scan within the same domain (if you're building a web tree, for example):public function getAttribute($url, $attr, $domain = NULL) { $result = array(); $elements = $this->getContent($url) ->getElementsByTagName('*'); foreach ($elements as $node) { if ($node->hasAttribute($attr)) { $value = $node->getAttribute($attr); if ($domain) { if (stripos($value, $domain) !== FALSE) { $result[] = trim($value); } } else { $result[] = trim($value); } } } return $result; }
In order to use the new Hoover
class, initialize the autoloader (described previously) and create an instance of the Hoover
class. You can then run the Hoover::getTags()
method to produce an array of tags from the URL you specify as an argument.
Here is a block of code from chap_01_vacuuming_website.php
that uses the Hoover
class to scan the O'Reilly website for <A>
tags:
<?php // modify as needed define('DEFAULT_URL', 'http://oreilly.com/'); define('DEFAULT_TAG', 'a'); require __DIR__ . '/../Application/Autoload/Loader.php'; Application\Autoload\Loader::init(__DIR__ . '/..'); // get "vacuum" class $vac = new Application\Web\Hoover(); // NOTE: the PHP 7 null coalesce operator is used $url = strip_tags($_GET['url'] ?? DEFAULT_URL); $tag = strip_tags($_GET['tag'] ?? DEFAULT_TAG); echo 'Dump of Tags: ' . PHP_EOL; var_dump($vac->getTags($url, $tag));
The output will look something like this:

For more information on DOM, see the PHP reference page at http://php.net/manual/en/class.domdocument.php.
Sometimes you need to scan a website, but go one level deeper. For example, you want to build a web tree diagram of a website. This can be accomplished by looking for all <A>
tags and following the HREF
attributes to the next web page. Once you have acquired the child pages, you can then continue scanning in order to complete the tree.
A core component of a deep web scanner is a basic
Hoover
class, as described previously. The basic procedure presented in this recipe is to scan the target website and hoover up all theHREF
attributes. For this purpose, we define aApplication\Web\Deep
class. We add a property that represents the DNS domain:namespace Application\Web; class Deep { protected $domain;
Next, we define a method that will hoover the tags for each website represented in the scan list. In order to prevent the scanner from trawling the entire World Wide Web (WWW), we've limited the scan to the target domain. The reason why
yield from
has been added is because we need to yield the entire array produced byHoover::getTags()
. Theyield from
syntax allows us to treat the array as a sub-generator:public function scan($url, $tag) { $vac = new Hoover(); $scan = $vac->getAttribute($url, 'href', $this->getDomain($url)); $result = array(); foreach ($scan as $subSite) { yield from $vac->getTags($subSite, $tag); } return count($scan); }
Note
The use of
yield from
turns thescan()
method into a PHP 7 delegating generator. Normally, you would be inclined to store the results of the scan into an array. The problem, in this case, is that the amount of information retrieved could potentially be massive. Thus, it's better to immediately yield the results in order to conserve memory and to produce immediate results. Otherwise, there would be a lengthy wait, which would probably be followed by an out of memory error.In order to keep within the same domain, we need a method that will return the domain from the URL. We use the convenient
parse_url()
function for this purpose:public function getDomain($url) { if (!$this->domain) { $this->domain = parse_url($url, PHP_URL_HOST); } return $this->domain; }
First of all, go ahead and define the Application\Web\Deep
class defined previously, as well as the Application\Web\Hoover
class defined in the previous recipe.
Next, define a block of code from chap_01_deep_scan_website.php
that sets up autoloading (as described earlier in this chapter):
<?php // modify as needed define('DEFAULT_URL', unlikelysource.com'); define('DEFAULT_TAG', 'img'); require __DIR__ . '/../../Application/Autoload/Loader.php'; Application\Autoload\Loader::init(__DIR__ . '/../..');
Next, get an instance of our new class:
$deep = new Application\Web\Deep();
At this point, you can retrieve URL and tag information from URL parameters. The PHP 7 null coalesce
operator is useful for establishing fallback values:
$url = strip_tags($_GET['url'] ?? DEFAULT_URL); $tag = strip_tags($_GET['tag'] ?? DEFAULT_TAG);
Some simple HTML will display results:
foreach ($deep->scan($url, $tag) as $item) { $src = $item['attributes']['src'] ?? NULL; if ($src && (stripos($src, 'png') || stripos($src, 'jpg'))) { printf('<br><img src="%s"/>', $src); } }
For more information on generators and yield from
, please see the article at http://php.net/manual/en/language.generators.syntax.php.
For the most part, PHP 5.x code can run unchanged on PHP 7. There are a few changes, however, that are classified as backwards incompatible. What this means is that if your PHP 5 code is written in a certain way, or uses functions that have been removed, your code will break, and you'll have a nasty error on your hands.
The PHP 5 to PHP 7 Code Converter does two things:
Scans your code file and converts PHP 5 functionality that has been removed to its equivalent in PHP 7
Adds comments with
//
WARNING
where changes in language usage have occurred, but where a re-write is not possible
The core of this recipe is the new PHP 7 preg_replace_callback_array()
function. What this amazing function allows you to do is to present an array of regular expressions as keys, with the value representing an independent callback. You can then pass the string through a series of transformations. Not only that, the subject of the array of callbacks can itself be an array.
In a new class
Application\Parse\Convert
, we begin with ascan()
method, which accepts a filename as an argument. It checks to see if the file exists. If so, it calls the PHPfile()
function, which loads the file into an array, with each array element representing one line:public function scan($filename) { if (!file_exists($filename)) { throw new Exception( self::EXCEPTION_FILE_NOT_EXISTS); } $contents = file($filename); echo 'Processing: ' . $filename . PHP_EOL; $result = preg_replace_callback_array( [
Next, we start passing a series of key/value pairs. The key is a regular expression, which is processed against the string. Any matches are passed to the callback, which is represented as the value part of the key/value pair. We check for opening and closing tags that have been removed from PHP 7:
// replace no-longer-supported opening tags '!^\<\%(\n| )!' => function ($match) { return '<?php' . $match[1]; }, // replace no-longer-supported opening tags '!^\<\%=(\n| )!' => function ($match) { return '<?php echo ' . $match[1]; }, // replace no-longer-supported closing tag '!\%\>!' => function ($match) { return '?>'; },
Next is a series of warnings when certain operations are detected and there is a potential code-break between how they're handled in PHP 5 versus PHP 7. In all these cases, the code is not re-written. Instead, an inline comment with the word
WARNING
is added:// changes in how $$xxx interpretation is handled '!(.*?)\$\$!' => function ($match) { return '// WARNING: variable interpolation . ' now occurs left-to-right' . PHP_EOL . '// see: http://php.net/manual/en/' . '// migration70.incompatible.php' . $match[0]; }, // changes in how the list() operator is handled '!(.*?)list(\s*?)?\(!' => function ($match) { return '// WARNING: changes have been made ' . 'in list() operator handling.' . 'See: http://php.net/manual/en/' . 'migration70.incompatible.php' . $match[0]; }, // instances of \u{ '!(.*?)\\\u\{!' => function ($match) { return '// WARNING: \\u{xxx} is now considered ' . 'unicode escape syntax' . PHP_EOL . '// see: http://php.net/manual/en/' . 'migration70.new-features.php' . '#migration70.new-features.unicode-' . 'codepoint-escape-syntax' . PHP_EOL . $match[0]; }, // relying upon set_error_handler() '!(.*?)set_error_handler(\s*?)?.*\(!' => function ($match) { return '// WARNING: might not ' . 'catch all errors' . '// see: http://php.net/manual/en/' . '// language.errors.php7.php' . $match[0]; }, // session_set_save_handler(xxx) '!(.*?)session_set_save_handler(\s*?)?\((.*?)\)!' => function ($match) { if (isset($match[3])) { return '// WARNING: a bug introduced in' . 'PHP 5.4 which ' . 'affects the handler assigned by ' . 'session_set_save_handler() and ' . 'where ignore_user_abort() is TRUE . 'has been fixed in PHP 7.' . 'This could potentially break ' . 'your code under ' . 'certain circumstances.' . PHP_EOL . 'See: http://php.net/manual/en/' . 'migration70.incompatible.php' . $match[0]; } else { return $match[0]; } },
Any attempts to use
<<
or>>
with a negative operator, or beyond 64, is wrapped in atry { xxx } catch() { xxx }
block, looking for anArithmeticError
to be thrown:// wraps bit shift operations in try / catch '!^(.*?)(\d+\s*(\<\<|\>\>)\s*-?\d+)(.*?)$!' => function ($match) { return '// WARNING: negative and ' . 'out-of-range bitwise ' . 'shift operations will now . 'throw an ArithmeticError' . PHP_EOL . 'See: http://php.net/manual/en/' . 'migration70.incompatible.php' . 'try {' . PHP_EOL . "\t" . $match[0] . PHP_EOL . '} catch (\\ArithmeticError $e) {' . "\t" . 'error_log("File:" . $e->getFile() . " Message:" . $e->getMessage());' . '}' . PHP_EOL; },
Next, the converter rewrites any usage of
call_user_method*()
, which has been removed in PHP 7. These are replaced with the equivalent usingcall_user_func*()
:// replaces "call_user_method()" with // "call_user_func()" '!call_user_method\((.*?),(.*?)(,.*?)\)(\b|;)!' => function ($match) { $params = $match[3] ?? ''; return '// WARNING: call_user_method() has ' . 'been removed from PHP 7' . PHP_EOL . 'call_user_func(['. trim($match[2]) . ',' . trim($match[1]) . ']' . $params . ');'; }, // replaces "call_user_method_array()" // with "call_user_func_array()" '!call_user_method_array\((.*?),(.*?),(.*?)\)(\b|;)!' => function ($match) { return '// WARNING: call_user_method_array()' . 'has been removed from PHP 7' . PHP_EOL . 'call_user_func_array([' . trim($match[2]) . ',' . trim($match[1]) . '], ' . $match[3] . ');'; },
Finally, any attempt to use
preg_replace()
with the/e
modifier is rewritten using apreg_replace_callback()
:'!^(.*?)preg_replace.*?/e(.*?)$!' => function ($match) { $last = strrchr($match[2], ','); $arg2 = substr($match[2], 2, -1 * (strlen($last))); $arg1 = substr($match[0], strlen($match[1]) + 12, -1 * (strlen($arg2) + strlen($last))); $arg1 = trim($arg1, '('); $arg1 = str_replace('/e', '/', $arg1); $arg3 = '// WARNING: preg_replace() "/e" modifier . 'has been removed from PHP 7' . PHP_EOL . $match[1] . 'preg_replace_callback(' . $arg1 . 'function ($m) { return ' . str_replace('$1','$m', $match[1]) . trim($arg2, '"\'') . '; }, ' . trim($last, ','); return str_replace('$1', '$m', $arg3); }, // end array ], // this is the target of the transformations $contents ); // return the result as a string return implode('', $result); }
To use the converter, run the following code from the command line. You'll need to supply the filename of the PHP 5 code to be scanned as an argument.
This block of code, chap_01_php5_to_php7_code_converter.php
, run from the command line, calls the converter:
<?php // get filename to scan from command line $filename = $argv[1] ?? ''; if (!$filename) { echo 'No filename provided' . PHP_EOL; echo 'Usage: ' . PHP_EOL; echo __FILE__ . ' <filename>' . PHP_EOL; exit; } // setup class autoloading require __DIR__ . '/../Application/Autoload/Loader.php'; // add current directory to the path Application\Autoload\Loader::init(__DIR__ . '/..'); // get "deep scan" class $convert = new Application\Parse\Convert(); echo $convert->scan($filename); echo PHP_EOL;
For more information on backwards incompatible changes, please refer to http://php.net/manual/en/migration70.incompatible.php.