How does [modus operandi] autoload work in PHP?

3

Suppose I have the following file structure in the root of my site:

index.php
autoload.php

----| /Models
--------| /MainModel
------------| MainModel.php

----| /Controllers
--------| /MainController
------------| MainController.php

Suppose there is a method in MainModel.php called mainMethod () as follows:

<?php namespace Models\MainModel;

class MainModel
{

    public function mainMethod()
    {
        return json_encode(array('mensagem' => 'Tudo funcionando por aqui'));
    }
}

In index.php , I include the file autoload.php

<?php

include_once 'autoload.php';

// resto do código

My autoload.php file has the following code:

spl_autoload_register(function ($class) {

    $prefix = '';

    $base_dir = __DIR__.'/';

    $len = strlen($prefix);
    if (strncmp($prefix, $class, $len) !== 0) {
        return;
    }

    $relative_class = substr($class, $len);

    $file = $base_dir.str_replace('\', '/', $relative_class).'.php';

    if (file_exists($file)) {
        require $file;
    }
});

Now, comes witchcraft that I do not understand how it works. If I do:

<?php

include_once 'autoload.php';

$obj = new Models\MainModel\MainModel;


echo $obj->mainMethod();

// Output: {"mensagem": "Tudo funcionando por aqui"}

Even if I indicate the classes of controllers the recognition is the same. It works!

Well, I have a closure autoload that works, but how does this closure work? How does PHP's autoloading recognize classes within folders, even though I have indicated only the project root?

PHP is going inside the folders and recognizing the classes, how is this possible?

The big detail of this is that if the MainModel.php file is not inside a MainModel folder (with the same file name) the autoload does not work.

Another curious thing is that the namespace have to indicate the path of the file to the class from the root Models\MainModel , then I declare the class.

I do not want to know how to use autoload, because this I already got, but rather how it works, because I'm using something I do not know how it works.

My question is completely different from that of moderates in the site: see "duplicate possible" / a>

    
asked by anonymous 04.01.2017 / 20:42

1 answer

6

spl_autoload_register intercepts all your calls that are made like this:

  • $foo = new AlgumaCoisa(); instantiate a class
  • echo AlgumaCoisa::teste; get a constant
  • echo AlgumaCoisa::$teste; get a static variable
  • $baz = AlgumaCoisa::funcao(); calls a static method

It functions as a "proxy", it runs before completing the class instance for example.

Now I recommend not using require or require_once , nor file_exists , do so:

if (is_file($file)) {
    include_once $file;
}

This is because it can affect the errors, because if the expected error is Class not found when the class does not exist your code will issue a require error, which is correct, but not for the PSR.

And file_exists checks files and folders, if it has a folder named qualquercoisa.php it can break the application, is_file does not have significant performance difference if compared file_exists , maybe only execute 100.000 classes at the same time you lose 0.0002 seconds.

I recommend using one of these examples:

If it's PSR-4 it should look like this, (I adapted it to your case and simplified it well by removing what I believe would not be used):

<?php
spl_autoload_register(function($class) {
    $class = ltrim($class, '\');

    $file = str_replace('\', '/', $class) . '.php';

    if (is_file($file)) {
        include_once $file;
    }
});

of factor use an anonymous function or is not indifferent.

Understanding the code

As I said you write spl_autoload as you wish, there are several ways to implement it, but I'll explain the code I wrote:

  • Remove% with% from start (it's a bug in php 5.3, in other versions this problem does not exist, so basically it's a "backward compatibility"):

    $class = ltrim($class, '\');
    
  • For example, if \ gets $class , this will change Models\MainModel\MainModel by \ :

    $file = str_replace('\', '/', $class) . '.php';
    

    And it generates this in the variable / :

    "Models/MainModel/MainModel.php"
    

Then from the value of $file we will check if the file exists and if it exists it will include:

if (is_file($file)) {
    include_once $file;
}

And then the process of calling $file will proceed

    The main detail of this is that if the MainModel.php file is not inside a MainModel folder (with the same filename) autoload does not work.

    p>

    P: Another curious thing is that namespaces have to indicate the file path to the class from the Models \ MainModel root, then I declare the class.

    A: This is relative, you implement it however you want, but in almost all languages the namespace is represented by a structure of folders and files, it has languages that do not, such as C + +, where the file can be anywhere, because its include is not done at the moment it uses the class, but when using new Models\MainModel\MainModel

04.01.2017 / 20:46