<? php
include ( 'global.php' );
// Here is the page code
?>
<? php
// Do something here
?>
RewriteEngine on RewriteCond% {REQUEST_FILENAME}! -F RewriteCond% {REQUEST_FILENAME}! -D RewriteRule ^ (. *) $ Index.php? Route = $ 1 [L, QSA]
<? php
error_reporting ( E_ALL );
if ( version_compare ( phpversion (), '5.1.0' , '<' ) == true ) {die ( 'PHP5.1 Only' ); }
// Constants:
define ( 'DIRSEP' , DIRECTORY_SEPARATOR );
// Find the path to the site files
$ site_path = realpath ( dirname ( __FILE__ ). DIRSEP . '..' . DIRSEP ). DIRSEP ;
define ( 'site_path' , $ site_path );
In this example, we declare some constant, find out where the system files are, and also check that the PHP version is, well, at least, 5.1.
The next thing to do is the Registry object (log, registry) to store global values. It will be transferred to individual objects of the system and used to access global values, without needing to designate variables as “global” or access $ GLOBALS array. Read the article “Using global values ​​in PHP” for more detailed information about the registry object.
Add the following code to the startup.php file after the code in the previous example:$ registry = new Registry ;
If you try to start the system now, you can see the following error:Fatal error: Class 'Registry' not found in g:\Projects\PHPit\content\simple mvc php5\demo\includes\startup.php on line 12
This, of course, is not a big surprise for us, because we have not written the Registry class itself yet. The file with the class could simply be connected using the include () function ( Note. Lane: by the way, include () is not such a function, but still an expression of the language, a control structure, if you look at the mana ), but let's use one of the new features in PHP 5: __autoload ().
The magic function __autoload () is used to dynamically load classes. When PHP detects a non-existent class, it first calls the __autoload () function and then throws an error. We can use this opportunity to load classes on the fly.
Paste this code before the code from the previous example:// Load classes on the fly
function __autoload ( $ class_name ) {
$ filename = strtolower ( $ class_name ). '.php' ;
$ file = site_path . 'classes' . DIRSEP . $ filename ;
if ( file_exists ( $ file ) == false ) {
return false ;
}
include ( $ file );
}
Our __autoload () function takes the class name passed as an argument to it and checks if there is a file with a similar name in the class directory. If the file does not exist, the function simply returns false and a fatal error pops up. But if the file exists, it will be loaded. Those. the required class will appear, and there will be no error.
We have not yet created the Registry class itself, so the error will still appear. Let's do it.Creating a Registry Class
The Registry class is used to transfer global values ​​between individual objects. This is actually a fairly simple class in which you need to implement several small methods.
First, create a classes directory and a registry.php file in it. Paste the following code into registry.php:<? php
Class Registry {
private $ vars = array ();
}
?>
Now we have the “skeleton” of the Registry class and we need to load it with methods. Write 2 methods: set () to set values ​​and get () to get values. You can also write a remove () method to remove values. Add these methods to the Registry class:function set ( $ key , $ var ) {
if (isset ( $ this -> vars [ $ key ]) == true ) {
throw new Exception ( 'Unable to set var `' . $ key . '`. Already set.' );
}
$ this -> vars [ $ key ] = $ var ;
return true ;
}
function get ( $ key ) {
if (isset ( $ this -> vars [ $ key ]) == false ) {
return null ;
}
return $ this -> vars [ $ key ];
}
function remove ( $ var ) {
unset ( $ this -> vars [ $ key ]);
}
?>
These methods are simple; they set, get, and remove elements from the $ vars array, which is an attribute of a class. In the set () method, we at the same time check whether a value with the specified key already exists, and, if it exists, we generate an exception. This is to avoid accidentally overwriting values.
Now we have a full Registry class, but we will not stop there. We use one of the features of the library SPL: ArrayAccess. SPL (abbreviated from Standard PHP Library, Standard PHP Library) is a collection of interfaces and classes designed to solve common problems. One of the SPL interfaces, ArrayAccess, can be used to provide access to an object as if it were a regular array. Let's look at this example:<? php
$ registry = new Registry ;
// Set some value
$ registry -> set ( 'name' , 'Dennis Pallett' );
// Get the value using get ()
echo $ registry -> get ( 'name' );
// Get the value using access as an array
echo $ registry [ 'name' ]
?>
The trick is that $ registry becomes like an array, although in reality it is an object. Of course, ArrayAccess does not give any special advantages, but it allows you to reduce the amount of code, since you do not have to write "-> get ()" every time. To use this interface, you need to fix the first line of the class ("Class Registry") as follows:Class Registry Implements ArrayAccess {
The keyword “Implements” tells the interpreter that we are implementing this class of interface, which is actually ArrayAccess.
The class that implements the ArrayAccess interface should have the following methods:function offsetExists ( $ offset ) {
return isset ( $ this -> vars [ $ offset ]);
}
function offsetGet ( $ offset ) {
return $ this -> get ( $ offset );
}
function offsetSet ( $ offset , $ value ) {
$ this -> set ( $ offset , $ value );
}
function offsetUnset ( $ offset ) {
unset ( $ this -> vars [ $ offset ]);
}
These methods should be self-explanatory. Further information can be found in the SPL documentation.
Now, having implemented the ArrayAccess interface, we can access the object as a regular array. This is clearly demonstrated, both in the previous example and in this:<? php
$ registry = new Registry ;
// Set some value
$ registry [ 'name' ] = 'Dennis Pallett' ;
// Get the value using get ()
echo $ registry -> get ( 'name' );
// Get the value using access as an array
echo $ registry [ 'name' ]
?>
The Registry class is now complete, and if you try to start the system, everything should work (although nothing will be output yet). We are done with the initialization file, and we can proceed to the next step of writing our MVC system: implementing database access, which is called “Model” in the MVC architecture.Model
An “M” or model is part of the MVC system, which is responsible for querying the database (or another external source) and providing information to the controller. It would be possible to load the required model depending on the request, but I prefer to slightly erase the boundaries between the model and the controller in this particular place, i.e. the controller works with the database directly through the library of interaction with the database, rather than through a separate model. Maybe you want to do it differently, it's a matter of taste.
We need to write the code necessary to establish the connection with the database and place it in index.php. There are many great libraries for working with databases (including my own, AutoCRUD ), but in PHP 5 there is already such a library - PDO. Therefore, there is no need to use any other.
Paste the following code into the index.php file (after connecting the initialization file):# Connecting to DB
$ db = new PDO ( 'mysql: host = localhost; dbname = demo' , '[user]' , '[password]' );
$ registry -> set ( 'db' , $ db );
In this example, we first create a new instance of the PDO library and connect to our MySQL database. Then we make the $ db variable globally accessible using our Registry class.
The model component of our system is ready, so let's move on to writing a controller.
Writing a controller also means writing the Router class responsible for loading the desired controller depending on the request (remember, the $ route variable is passed to the index.php URL).Class router
The Router class will parse the request, and then load the required controller. Create a "skeleton" class:<? php
Class Router {
private $ registry ;
private $ path ;
private $ args = array ();
function __construct ( $ registry ) {
$ this -> registry = $ registry ;
}
}
?>
Then add the following lines to index.php:# Download router
$ router = new Router ( $ registry );
$ registry -> set ( 'router' , $ router );
We have added the Router class to our MVC system, but it is not doing anything yet, so let's add the methods necessary for our work to it.
The first thing we write is the setPath () method to set the directory where all our controllers will be located. The method is as follows and should be added to the Router class:function setPath ( $ path ) {
$ path = trim ( $ path , '/ \\' );
$ path . = DIRSEP ;
if ( is_dir ( $ path ) == false ) {
throw new Exception ( 'Invalid controller path: `' . $ path . '`' );
}
$ this -> path = $ path ;
}
Then add the following lines to index.php:$ router -> setPath ( site_path . 'controllers' );
Now that we have set the path to our controllers, we will write the method responsible for loading the controller. This method will be called delegate (), it will analyze the request. The first piece of this method is:function delegate () {
// Analyze the path
$ this -> getController ( $ file , $ controller , $ action , $ args );
As you can see, it uses another method, getController (), to get the name of the controller and several other variables. This method looks like this:private function getController (& $ file , & $ controller , & $ action , & $ args ) {
$ route = (empty ( $ _GET [ 'route' ]))? '' : $ _GET [ 'route' ];
if (empty ( $ route )) { $ route = 'index' ; }
// Get the separate parts
$ route = trim ( $ route , '/ \\' );
$ parts = explode ( '/' , $ route );
// Find the right controller
$ cmd_path = $ this -> path ;
foreach ( $ parts as $ part ) {
$ fullpath = $ cmd_path . $ part ;
// Is there a folder with this path?
if ( is_dir ( $ fullpath )) {
$ cmd_path . = $ part . DIRSEP ;
array_shift ( $ parts );
continue;
}
// Find the file
if ( is_file ( $ fullpath . '.php' )) {
$ controller = $ part ;
array_shift ( $ parts );
break;
}
}
if (empty ( $ controller )) { $ controller = 'index' ; };
// Get the action
$ action = array_shift ( $ parts );
if (empty ( $ action )) { $ action = 'index' ; }
$ file = $ cmd_path . $ controller . '.php' ;
$ args = $ parts ;
}
Let's run through this method. First, it takes the value of the $ route variable from the request, then splits it into pieces using the explode () function. For example, the query “members / view” is converted to the following array: array ('members', 'view').
Then, using the foreach loop, it passes through each part and checks whether this part is a directory. If so, he attributes it to the path to the file and checks the next part. This allows you to put controllers in subdirectories and, thus, get a hierarchy of controllers. If the current part of the request is not a directory, but is a file, it is stored in the $ controller variable, and we exit the loop, because we have found the controller that we need.
After the loop, we check the variable with the name of the controller. If it is empty, then we use the “index” controller, which will be our default controller. Then the method determines the action to be performed. A controller is a class that consists of several methods. The action points to a specific method. If no action is specified, we will use “index” - the default action.
And finally, we get the full path to the controller file, combining three variables: the path, the name of the controller, and the extension "php".
Now that we have analyzed the request, it's time to call the delegate () method to load the controller and execute the action. The complete delegate () method looks like this:function delegate () {
// Analyze the path
$ this -> getController ( $ file , $ controller , $ action , $ args );
// Is the file available?
if ( is_readable ( $ file ) == false ) {
die ( '404 Not Found' );
}
// Connect the file
include ( $ file );
// Create a controller instance
$ class = 'Controller_' . $ controller ;
$ controller = new $ class ( $ this -> registry );
// Is the action available?
if ( is_callable (array ( $ controller , $ action )) == false ) {
die ( '404 Not Found' );
}
// perform the action
$ controller -> $ action ();
}
After analyzing the request using the getController () method, we check whether the file actually exists and, if not, return a simple error message.
After that we connect the file with the controller and create an instance of its class, which should be called “Controller_ [name]”. A little later we will talk about controllers in more detail.
Then we check if there is a specified action (i.e. a method) and if it is possible to access it (use the is_callable () function for this). Finally, we perform the actual action itself, on which the role of the Router class ends.
Having written the delegate () method completely, we will add the following line to the index.php file:$ router -> delegate ();
If we try to start the system now, we will see the following error (of course, if there are no controllers directory yet):Fatal error: Uncaught exception 'Exception' with message 'Invalid controller path: `g:\Projects\PHPit\content\simple mvc php5\demo\controllers\`' in g:\Projects\PHPit\content\simple mvc php5\demo\classes\router.php:18 Stack trace: #0 g:\Projects\PHPit\content\simple mvc php5\demo\index.php(13): Router->setPath('g:\Projects\PHP...') #1 {main} thrown in g:\Projects\PHPit\content\simple mvc php5\demo\classes\router.php on line 18
Or we will see the error “404 Not Found”, as there is not a single controller yet. But this is what we are going to do now.Controller
The controllers in our MVC system will be fairly simple and take quite a bit of time. First, make sure that the controllers directory exists. Create a file controller_base.php in the classes directory and paste the following code into it:<? php
Abstract Class Controller_Base {
protected $ registry ;
function __construct ( $ registry ) {
$ this -> registry = $ registry ;
}
abstract function index ();
}
?>
This abstract class will be the parent class for all of our controllers. It will do only two things: save a local copy of the Registry class and, using the abstract index () method, force all child controllers to implement this method.
Let's write our first controller. Create an index.php file in the controllers directory and paste the following code into it:<? php
Class Controller_Index Extends Controller_Base {
function index () {
echo 'Hello from my MVC system' ;
}
}
?>
We have just created our first controller and, if we try to start the system, we can see the following:
(Full size, 1024x357, 115 KB)
This means that the Router class performed its work and launched the required action from the required controller. Let's write another controller that will match the query "members / view". Create a file members.php in the controller directory and paste the following code into it:<? php
Class Controller_Members Extends Controller_Base {
function index () {
echo 'Default index of the members `controllers' ;
}
function view () {
echo 'You are viewing the members / view request' ;
}
}
?>
Now we’ll go to our MVC system at the request of "members / view" or "index.php? Route = members / view". We should see the following result:
(Full size, 1024x320, 117 KB)
Only by writing a new controller and adding a method to it, we were able to create a new page, and nothing had to be changed in the system itself. In addition, our controllers do not need to include the global.php file or do something like that.
Now that we have controllers, only one thing remains: "V" or "View" ("Display").Display
As is the case with models, there are several different options for creating a View component in an MVC system. We could teach the Router class to automatically load another file, named something like this: “view_ {name} .php”. But in order to make the manual more understandable, we will write the Template class, which will deal with the output of templates.
First, create a file template.php in the classes directory and paste the following code into it:<? php
Class Template {
private $ registry ;
private $ vars = array ();
function __construct ( $ registry ) {
$ this -> registry = $ registry ;
}
}
?>
Now we have the main structure of our Template class. The next step is to add this code to the index.php file right before the lines associated with the Router class:# Create a template object
$ template = new Template ( $ registry );
$ registry -> set ( 'template' , $ template );
Since we will need to use values ​​from models and controllers, we will write the set () method to set the variables available in the templates. Let's look at an example:function set ( $ varname , $ value , $ overwrite = false ) {
if (isset ( $ this -> vars [ $ varname ]) == true AND $ overwrite == false ) {
trigger_error ( 'Unable to set var `' . $ varname . '`. Already set, and not allowed.' , E_USER_NOTICE );
return false ;
}
$ this -> vars [ $ varname ] = $ value ;
return true ;
}
function remove ( $ varname ) {
unset ( $ this -> vars [ $ varname ]);
return true ;
}
The set () and remove () methods are fairly simple and are used, respectively, to set and remove variables.
Let's write the show () method that will display the templates. The easiest way is to create a separate templates directory where to store all template files, and use include () to output the template. Of course, your own show () method can be completely different and load templates from the database or do something else. Look at kuso
Source: https://habr.com/ru/post/31270/
All Articles