PHP Namespace & use & MVC

Lecture



A namespace is a set that means a model, an abstract repository, or an environment created for the logical grouping of unique identifiers (that is, names). An identifier defined in a namespace is associated with this space. The same identifier can be independently defined in several spaces. Thus, the value associated with an identifier defined in one namespace may (or may not) have the same (or rather, different) meaning, like the same identifier defined in another space. Namespace-supported languages ​​define rules that indicate to which namespace the identifier (that is, its definition) belongs. Wiki


All clear? In fact, everything is simple. Before version 5.3, php had only two spaces - global (in which your main code was executed) and local (in which function variables were defined).
  PHP Namespace & use & MVC
Since version 5.3 everything has changed. Now you can define your own namespace, in which your classes will exist, methods, etc.
  PHP Namespace & use & MVC
I hope it became a little clearer.

I specifically called classes the same way. Since they are defined in different spaces, they are two different classes, despite the same names. The main script, as before, functions in the global space, nothing has changed here, and in it, as before, classes and functions can be defined. So why then do we need spaces? First of all, to ensure that when you connect a file, with some framework or library, your classes will not override the framework classes or vice versa.

In order to use the classes defined in your namespace, you need in the right place (I usually prefer to do it at the beginning of the file) to import the space you defined into the global one. To do this, use the keyword
use 

Warning: for some reason, php does not allow the use of the use keyword in blocks of conditions and cycles.


Let's take an example from the pictures and put it in the code:
Attention: the namespase keyword must be located at the very beginning of the file immediately after <? php

A.php file
 <? php namespace A { class A { public static function say() { echo 'Я пространство имен А'; } } } 

B.php file
 <? php namespace B { class A { public static function say() { echo 'Я пространство имен B'; } } } 

Alternative syntax is possible:
 <? php namespace A; class A { public static function say() { echo 'Я пространство имен А'; } } 


It is recommended to declare each namespace in a separate file. Although it is possible and in one, but it is strictly not recommended!
Now let's move to the third file in which our main script will function.
index.php
 <? php require_once 'A.php'; require_once 'B.php'; use A\A; use B\A; 

it would seem to be an advantage, only the code was added, but this is not quite so, a little further I will give an example of the autoload class, with which the lines connecting files with classes will be unnecessary.
Now turn to our classes.
 <? php require_once 'A.php'; require_once 'B.php'; use A\A; use B\A; A\A::say(); B\A::say(); 

Attention: the use of the scope resolution operator (: :) in php namespaces is not allowed! The only thing for which it is suitable is to refer to static class methods and constants. Initially, they wanted to use it for the namespace, but then, because of the problems that arose, they refused. Therefore, the construction of the form A :: A :: say (); is invalid and will result in an error.

For namespaces, you must use the backslash character "\"
Attention: in order to avoid misunderstanding, it is necessary to escape this symbol when using it in the lines: '\\'


Namespaces can be nested into each other, add our A.php file:
 <? php namespace A { class A { public static function say() { echo 'Я пространство имен А'; } } } namespace A\subA { class A { public static function say() { echo 'Я подпространство имен А'; } } } 

and in the index we write the following:
 <? php require_once 'A.php'; require_once 'B.php'; use A\A as A; use B\A as B; use A\subA as sub A::say(); A::say(); sub::say(); 


An important point is the use of aliases for imported spaces. One could write A \ subA :: say (); agree, to write full paths to spaces each time is difficult in order to avoid this aliases were introduced. When compiling, the following will happen instead of the sub alias: A \ subA, so we get the call A \ subA :: say ();

And what then happens when calling functions defined in the global space? PHP first searches for a function inside the space where you are currently working, and if it does not find it, it refers to the global scope. In order to immediately indicate that you are using a global function, you must put a backslash in front of it.

In order to avoid problems with autoloading of classes from spaces, the file system should be organized similarly to the organization of spaces. For example, we have a root folder classes, where our classes will be stored, then our spaces can be organized as follows
classes \ A \ A.php
classes \ A \ sub \ A.php (subspace sub put in a separate file)
classes \ B \ B.php

In php there is a magic constant __NAMESPACE__ which contains the name of the current space.

And now about autoload.


The class below is not mine, I just made it a worker and improved it a little bit from here.
Attention: In order for your classes to load the class name must match the file name!


 <?php namespace yourNameSpace { class Autoloader { const debug = 1; public function __construct(){} public static function autoload($file) { $file = str_replace('\\', '/', $file); $path = $_SERVER['DOCUMENT_ROOT'] . '/classes'; $filepath = $_SERVER['DOCUMENT_ROOT'] . '/classes/' . $file . '.php'; if (file_exists($filepath)) { if(Autoloader::debug) Autoloader::StPutFile(('подключили ' .$filepath)); require_once($filepath); } else { $flag = true; if(Autoloader::debug) Autoloader::StPutFile(('начинаем рекурсивный поиск')); Autoloader::recursive_autoload($file, $path, &$flag); } } public static function recursive_autoload($file, $path, $flag) { if (FALSE !== ($handle = opendir($path)) && $flag) { while (FAlSE !== ($dir = readdir($handle)) && $flag) { if (strpos($dir, '.') === FALSE) { $path2 = $path .'/' . $dir; $filepath = $path2 . '/' . $file . '.php'; if(Autoloader::debug) Autoloader::StPutFile(('ищем файл <b>' .$file .'</b> in ' .$filepath)); if (file_exists($filepath)) { if(Autoloader::debug) Autoloader::StPutFile(('подключили ' .$filepath )); $flag = FALSE; require_once($filepath); break; } Autoloader::recursive_autoload($file, $path2, &$flag); } } closedir($handle); } } private static function StPutFile($data) { $dir = $_SERVER['DOCUMENT_ROOT'] .'/Log/Log.html'; $file = fopen($dir, 'a'); flock($file, LOCK_EX); fwrite($file, ('║' .$data .'=>' .date('dmY H:i:s') .'<br/>║<br/>' .PHP_EOL)); flock($file, LOCK_UN); fclose ($file); } } \spl_autoload_register('yourNameSpace\Autoloader::autoload'); } 

If you look at the names of the classes that come to be loaded, then you will see that each class is preceded by a prefix from the namespace, which is specified in use. That is why I recommend using the location of files in directories similar to the namespace, it speeds up the search to one or two iterations.

Now our index can be written like this:
 <? php require_once 'Autoloader.php'; use Autoloader as Autoloader; use A\A as A; use B\A as B; use A\subA as sub A::say(); A::say(); sub::say(); 

now all the classes and interfaces that you will use will be loaded automatically.

To demonstrate some of the dynamic capabilities of a language with spaces, let's declare another class:
test.php
 <? php namespace mySpace { class test { __construct() { //конструктор; } function sayName($name) { echo 'Привет ' . $name; } static function sayOther() { echo 'статичный вызов'; } } } 


index.php
 <? php require_once 'Autoloader.php'; use Autoloader as Autoloader; use mySpace\test as test //можно, например сделать так $class = 'test'; //приведет к вызову конструктора $obj = new $class; $obj->sayName('test'); //а можно так test\sayName('test2'); //или так $obj::sayName('test'); //а можно так test::sayName('test2'); 

PHP, starting with version 5.3, gave us a namespace. Since then, there has been a sluggish somewhere, but somewhere there is a heated discussion, how can we use this namespace?
Some frameworks such as Symphony, Laravel, and, of course, Zend have adopted this technology.
All this more or less fit into the MVC scheme. There is only one, probably eternal, discussion, what should be the main marriage couple of the application - Model and Controller?
Some of us are told that the Model should be long and thick and with it a slim and thin Controller. In a word - matriarchy.
Others, on the contrary, believe that the controller should manage and rule everything, so it turns out to be solid, well-fed. And with him a slim, slim Model, whose task comes down to bring-bring. Such is the patriarchy.
So what's better in the MVC scheme? Patriarchy or matriarchy?
Let's look at it from the point of view of building a family unit based on democracy. And let Namespace help us with this.

We don’t like thick, clumsy controllers, which, like an elephant in a china shop, can inadvertently crush the entire application.
We don't like fat models either. Well, who likes them? They must be worthy of the podium!
Let's try using Namespace, as with a good matchmaker, to create a harmonious family.

First, create an application framework. No matter how trite, but let it be a blog.

  PHP Namespace & use & MVC


We have created a basic structure, where:
  • Blog is the repository of our application;
  • Views and Templates - a repository of views and templates;
  • Utility - repository of shared libraries;
  • index.php - bootstrap script;
  • Post - this is where the family idyll of the Controller and Model should take place.

With index.php, everything is simple:
 <?php use Blog\Blog as Blog; /* * main application */ define ("APP_PATH", "/home/oleg/www/viper.dev/"); define ("VIEW_PATH", "/home/oleg/www/viper.dev/Blog/Views/"); spl_autoload_register(function ($class) { require_once str_replace('\\', '/', $class). '.php'; }); $blog = new Blog(); $blog->run(); /* * end of index.php */ 


We define the necessary paths and create an autoloader.
The autoloader loads the necessary classes that are located in the folder hierarchy according to the class namespace. For example, the Blog \ Post \ Services \ View class will be searched in Blog / Post / Services.
And here is the first meeting with Namespace.
When we start index.php, we create an instance of the Blog application, whose class is loaded from Blog / Blog.php.
Let's look at it.

 <?php namespace Blog; use Blog\Post\Post as Post; class Blog { public $post; public function __construct() { $this->post = new Post(); } public function run() { $this->post->view->all(); } }//end class Blog 

When creating the Blog class, we embed the Post class in it with Namespace Blog \ Post and the autoloader loads it from Blog / Post / Post.php.
Probably, this class can be called a controller,
 <?php namespace Blog\Post; use Blog\Post\Services\View as View; class Post { public $view; public function __construct() { $this->view = new View(); } }//end class Post 

Entity Post includes:
- the structure of the data record itself - Blog \ Post \ Entities \ PostEntity.php
 <?php namespace Blog\Post\Entities; class PostEntity { public $id; public $title; public $body; }//end class 

- services serving controller requests - Blog \ Post \ Services \ View.php (one of the services, for example)
 <?php namespace Blog\Post\Services; use Blog\Utility\Contemplate as Contemplate; use Blog\Post\Repositories\Db as DB; class View { public $db; public function __construct() { $this->db = new DB(); }//end __construct public function all() { $posts = $this->db->survey(); Contemplate::compose(array( 'header' => 'header', 'main' => 'main', 'footer' => 'footer', ), array( 'posts' => $posts, 'title' => 'Viper site', )); } }//end class PostView 

- database interaction system - Blog \ Post \ Repositories \ DB.php - this is our thin, elegant Model,
Just bring and bring, and nothing more!
 <?php namespace Blog\Post\Repositories; use PDO as PDO; class DB { private $dbh; public function __construct() { $user = 'user'; $pass = 'parole'; try { $this->dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass, array( PDO::ATTR_PERSISTENT => true )); } catch (PDOException $e) { echo "Error!: " . $e->getMessage() . "<br/>"; die(); } }//end __construct public function survey() { $query_view = $this->dbh->prepare('SELECT * from posts'); $query_view->execute(); return $query_view->fetchAll(PDO::FETCH_CLASS, "Blog\Post\Entities\PostEntity"); }//end survey }//end class Db 

As a result, we managed to create an application structure, where all components are well connected, and we achieved a clear separation of classes, where each class performs its task. Our controller is thin and powerful at the same time. Model to match him. Perfect family!
And all thanks to Namespace.

I do not argue, in many cases the framework is convenient. But look, does Namespace remind you anything?
A clear division into classes, a strict, and at the same time flexible, fully subordinate to the developer hierarchy of directories and classes.
The absence of such a weighty appendage in the form of hundreds of files and classes in the form of a framework.
Lack of procrustes bed rules for the interaction of classes and components.

The article is inspired by reflections on this topic by Taylor Otwell, author of the Laravel framework, for which he thanks a lot.
created: 2016-03-08
updated: 2021-03-13
132631



Rating 9 of 10. count vote: 2
Are you satisfied?:



Comments


To leave a comment
If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Running server side scripts using PHP as an example (LAMP)

Terms: Running server side scripts using PHP as an example (LAMP)