1.4. Let's start writing an MVC framework
We’ve described in enough detail what our framework should include, and now it’s time to implement it. First, you need a web server. You can try using Denwer, but you might need to upgrade PHP.
You can download Denwer with PHP 5.5.20 from this page:
https://drupalbook.org/ru/drupal/denwer-obnovlenie-php
or from GitHub:
https://github.com/levmyshkin/denwer-php-5.5.20
Our framework will start with the index.php
file, which returns instances from the registry, calls the appropriate controller, and passes it the registry results. The controller, in turn, can call the necessary models.
Implementing Patterns
There are several ways to implement the patterns we discussed. We’ll choose one approach now, but you may change the implementation of any pattern later.
Let’s get started. In a previous article, we went over the structure of our framework, so now we need to create folders based on that structure.
Now create the file Registry/registry.class.php
. In this file, we’ll write our registry class.
<?php /** * Registry Object * Implements the Registry and Singleton patterns */ class Registry { /** * Array of our objects * @access private */ private static $objects = array(); /** * Array of our settings * @access private */ private static $settings = array(); /** * Human-readable name of our framework * @access private */ private static $frameworkName = 'Framework version 0.1'; /** * Instance of our registry * @access private */ private static $instance; /** * Constructor for our registry * @access private */ private function __construct() { } /** * Singleton method for accessing the object * @access public * @return object */ public static function singleton() { if( !isset( self::$instance ) ) { $obj = __CLASS__; self::$instance = new $obj; } return self::$instance; } /** * Prevent cloning of the object */ public function __clone() { trigger_error( 'Cloning the registry is not permitted', E_USER_ERROR ); } /** * Stores an object in our registry * @param string $object Name of the object * @param string $key Key for the array * @return void */ public function storeObject( $object, $key ) { require_once('objects/' . $object . '.class.php'); self::$objects[ $key ] = new $object( self::$instance ); } /** * Gets an object from our registry * @param string $key Key in the array * @return object */ public function getObject( $key ) { if( is_object ( self::$objects[ $key ] ) ) { return self::$objects[ $key ]; } } /** * Stores a setting in our registry * @param string $data * @param string $key Key for the array * @return void */ public function storeSetting( $data, $key ) { self::$settings[ $key ] = $data; } /** * Retrieves a setting from the registry * @param string $key * @return string */ public function getSetting( $key ) { return self::$settings[ $key ]; } /** * Gets the framework name * @return string */ public function getFrameworkName() { return self::$frameworkName; } } ?>
So how does our Registry work and how does it store objects?
- Objects are stored as an array
- When a new object is added to the registry, the class file is also included, an object instance is created, and it’s added to the array
- Objects are retrieved by passing their key to the
getObject
method
How is creating another copy of the registry object prevented?
- The constructor is
private
, preventing direct instantiation - Cloning the object triggers an error
- If you want to access the registry object from anywhere in the framework, and can’t pass it directly, you can use the static
singleton
method (Registry::singleton()
) to get the instance
Now that we have the registry, let’s write index.php
to start the framework and use the registry.
Index.php
index.php
is the entry point of our framework.
Later, we’ll add support for human-readable URLs using the .htaccess
file. For now, let’s walk through the code in index.php
:
<?php /** * Framework * Framework loader - acts as a single point of access to the Framework */ // Start the session session_start(); // Define some constants // Define the root of the framework so it can be accessed easily in any script define( "APP_PATH", dirname( __FILE__ ) ."/" ); // This prevents scripts from being accessed outside our framework define( "FW", true ); /** * Magic autoload function * Automatically loads the required controller when needed * @param string the name of the class */ function __autoload( $class_name ) { require_once('Controllers/' . $class_name . '/' . $class_name . '.php' ); } // Load the registry require_once('Registry/registry.class.php'); $registry = Registry::singleton(); // Print the name of the framework to verify everything works print $registry->getFrameworkName(); exit(); ?>
If everything is correct, you should see the framework name displayed:
Framework version 0.1
Let’s summarize what index.php
currently does:
- Starts a session so we can store and access data throughout the framework
- Defines the root directory of the framework, allowing it to run from a subdirectory
- Sets up autoloading for controllers
- Includes the registry class
- Prints the name of our framework