PSR-4 on an MVC project or not?

Asked

Viewed 823 times

8

I have two codes, the first one uses spl_autoload_register and the other does not, however the second loads "automatically the class" also.

With spl_autoload_register

  • Uses namespaces to split MVC
  • You can create multiple levels of folders by following the idea of PSR-4
  • Controllers and Models may have the same name since each is within a different namespace.

Code:

spl_autoload_register(function ($class)
{
    $np = explode('\\', $class);

    $base = strtolower($np[0]);

    switch ($base) {
        case 'controller':
        case 'model':
            $base = 'application/' . $base . 's';
        break;
        default:
            return NULL;
    }

    array_shift($np);

    $relative_class = strtolower(implode('/', $np));

    $file = './' . $base . '/' . $relative_class . '.php';
    /*
     * resulta em algo como:
     * ./application/controllers/foo/test/user.php
     * ./application/models/foo/abc/user.php
     */

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

Calling an action from a controller:

$controller = new \Controller\foo\test\user;
$controller->profile();

Calling a model:

$model = new \Model\foo\test\user;

With methods

  • Does not use namespaces
  • "Eventually" may be easier to understand/use than the previous code
  • Controllers and Models may not have the same name, but it’s no problem, since we can use prefixes
  • Supports sub-folders.

Code:

<?php
class App
{
    static private function prepare($path)
    {
        $fp = explode('.', $path);

        return array(
            'name' => end($fp),
            'path' => implode('/', $fp)
        );
    }

    static public function model($name)
    {
        $data = self::prepare($name);

        if (is_file($data['path'])) {
            require_once './application/models/' . $data['path'] . '.php';
        }

        return new $data['name'];
    }

    static public function action($name, $action)
    {
        $data = self::prepare($name);

        if (is_file($data['path'])) {
            require_once './application/controllers/' . $data['path'] . '.php';
        }

        $controller = new $data['name'];
        $controller->$action;
    }
}

Calling an action from a controller:

App::action('foo.test.user', 'profile');

Calling a model:

$model = App::model('foo.abc.user');

My question is, should I use the simplest way without namespaces and spl_autoload or not? How can I work or improve these codes so that it makes use easier for the ultimate developer?

1 answer

8


My recommendation would be to directly use a autoloader that implements the PSR-4, as included in Composer. In addition to being a standard already fully adopted in the community, the configuration is simpler and there is no need to reinvent the wheel.

Considering that your examples are for teaching purposes, I have some considerations:


Utilise namespaces and spl_autoload_register makes it possible to create a more extensible structure for the application. In small projects it is rare to collide with the class name, but when starting to introduce external libraries, the chance of this occurring increases.

The concept of namespaces is common in other languages, such as Packages in Java, namespaces in C#, which makes the concept already known to the developers of software.

The spl_autoload_register does the require classes or interfaces only when necessary, including when extends or implements.

Currently your autoload function only supports classes within model and controller, because the default case gives a return in the autoload without returning any file.

I would remove the switch that adds the s to the controller and models and would allow autoload to accurately reflect the class namespace:

spl_autoload_register(function ($class)
{
    $np = explode('\\', $class);
    $relative_class = strtolower(implode(DIRECTORY_SEPARATOR, $np));

    $file = './' . $relative_class . '.php';

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

Although the second form seems to suit her needs, she is more limited than using the spl_autoload_register. It can work well with Models or Controllers simple, but I see the following problems:

  • Refactoring the application can become a difficult task as the reference to the class is a string delimited by points. By using the full class reference we can use an IDE to rename that class and change its references. With a string I don’t see a safe way to do this other than by using regular expressions throughout the project or a replace all. There is even a constant included in PHP 5.5 call ::class, that returns the complete class path with the namespace as string.

  • The class App isn’t exactly a autoloader. Only two actions are possible: return an instance of a given model or perform the action of controller. PHP will not automatically load the classes needed for your application, limited to the use of object orientation features such as inheritance and interfaces, unless the parent class or interface has already been included with a require, which makes the code even more complex than using the use and namespaces. See this example considering the following classes

    // classe bar em ./application/controllers/bar.php
    class bar{
    
        public function run(){ echo "Hello World"; }
    
    }
    
    // classe baz em ./application/controllers/baz.php
    //
    // só é garantido que essa classe irá funciona se descomentar a linha abaixo
    // require_once 'application/controllers/bar.php';      
    class baz extends bar {}
    
    // arquivo principal
    
    // Executar esse método nesse momento resultará em um erro, pois a classe bar ainda foi carregada na aplicação
    //App::action('baz', 'run');
    
    // Isso vai funcionar como esperado, irá escrever na tela
    App::action('bar', 'run');
    
    // Agora baz vai funcionar pois bar já foi carregado na linha acima
    App::action('baz', 'run');
    
  • Could you explain this point simple string strongly coupled to its folder structure., with psr-4 is not nearly the same thing, only namespaces coupled to folder structure?

  • I didn’t understand it either PHP will not automatically load the classes needed for your application, it does load the . php file automatically. I didn’t get this point either.

  • I’ll edit the answer here calmly @Guilhermenascimento.

  • I understand now, about this limited use of object orientation resources, this was the most important point of his reply and certainly the best, I did not know this class Foo \Controller\exemplo {} fires the spl_autoload, tested and worked. -- I miss my reading the php documentation rs

  • Could put an example (without the need to write spl code) class baz extends bar explaining that extends bar also works with spl. Other than this my first code I did based on psr-4 (Closure Example), could make any suggestion/review/criticism of it?

  • @Guilhermenascimento is all right in using the clousure along with the spl_autoload_register . The only comment I have is that its code is not compatible with versions between PHP 5.1 (where SLP was introduced) and version 5.3 (which is where clousures appeared). Other than that I don’t see a problem.

  • but I tested the code in php5.3 and it worked normal, only it doesn’t work in php5.0 until 5.1 because these don’t support namespaces.

  • True, since namespaces are used, the code already requires the minimum version to be 5.3. If you were to use only spl_autoloader (without clousure) that would be supported in 5.1 and 5.2

  • correcting my last comment, 5.0, 5.1 and 5.2. : ) Thanks for the tip from extends, implements, etc was what made me decide.

  • Just to comment, so when you said it works on 5.3, I did it without the clousure after. Thank you

Show 5 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.