Book Image

PHP 7 Programming Cookbook

By : Doug Bierer
Book Image

PHP 7 Programming Cookbook

By: Doug Bierer

Overview of this book

PHP 7 comes with a myriad of new features and great tools to optimize your code and make your code perform faster than in previous versions. Most importantly, it allows you to maintain high traffic on your websites with low-cost hardware and servers through a multithreading web server. This book demonstrates intermediate to advanced PHP techniques with a focus on PHP 7. Each recipe is designed to solve practical, real-world problems faced by PHP developers like yourself every day. We also cover new ways of writing PHP code made possible only in version 7. In addition, we discuss backward-compatibility breaks and give you plenty of guidance on when and where PHP 5 code needs to be changed to produce the correct results when running under PHP 7. This book also incorporates the latest PHP 7.x features. By the end of the book, you will be equipped with the tools and skills required to deliver efficient applications for your websites and enterprises.
Table of Contents (22 chapters)
PHP 7 Programming Cookbook
Credits
Foreword
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Implementing class autoloading


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.

Getting ready

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.

How to do it...

  1. 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 file Loader.php and place it in the /path/to/cookbook/files/Application/Autoload folder.

  2. The first method we will present simply loads a file. We use file_exists() to check before running require_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;
    }
  3. We can then test the return value of loadFile() in the calling program and loop through a list of alternate directories before throwing an Exception if it's ultimately unable to load the file.

    Tip

    You will notice that the methods and properties in this class are static. This gives us greater flexibility when registering the autoloading method, and also lets us treat the Loader class like a Singleton.

  4. 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;
    }
  5. 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, an Exception is thrown.

  6. 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 the self::$dirs array:

    public static function addDirs($dirs)
    {
        if (is_array($dirs)) {
            self::$dirs = array_merge(self::$dirs, $dirs);
        } else {
            self::$dirs[] = $dirs;
        }
    }  
  7. 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 using spl_autoload_register() with the init() method:

    public static function init($dirs = array())
    {
        if ($dirs) {
            self::addDirs($dirs);
        }
        if (self::$registered == 0) {
            spl_autoload_register(__CLASS__ . '::autoload');
            self::$registered++;
        }
    }
  8. At this point, we can define __construct(), which calls self::init($dirs). This allows us to also create an instance of Loader if desired:

    public function __construct($dirs = array())
    {
        self::init($dirs);
    }

How it works...

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();