⬆️ ⬇️

Creating a modular structure using inversion control

In this article I will talk about how to create an easily expandable, modular structure. A similar organization is used in symfony . We will also use Composer . What it is and how to use it can be read here .



So, our modular structure will be based primarily on the principles of inversion of control . We will use IoC containers and my own library .



Start by creating a module management library. I called it Modular .



We first describe composer.json:

{ "name":"elfet/modular", "type":"library", "autoload": { "psr-0": { "Modular": "src/" } }, "require":{ "php":">=5.3.0", "elfet/ioc":"dev-master" } } 


Now where we will use “modular” we will have to connect IoC.

')

The estimated structure of our modular system will be:

 index.php -    app/ app.ini -   ModuleOne/ module.ini -   ModuleTwo/ 




We describe the class of the front controller App:

 namespace Modular; use IoC\Container; use Composer\Autoload\ClassLoader; //    /vendor/autoload.php class App { protected $rootDir; //    app/ protected $ioc; //  ioc  protected $loader; //  . public function __construct($rootDir, ClassLoader $classLoader) { $this->rootDir = $rootDir; $this->ioc = Container::getInstance(); $this->loader = new Loader($this->ioc, $classLoader); } public function load() { $appConfig = parse_ini_file($this->rootDir . '/app.ini', true); //     app.ini //       //   . foreach ($appConfig as $module => $config) { //       Modular\Module $config = array_merge(array( 'class' => 'Modular\Module', 'path' => $this->rootDir . '/module/' . $module, ), $config); //   $this->loader->load( $module, $config['class'], $this->rootDir . '/' . $config['path'] ); } } public function run() { $this->load(); } } 




Let's see how the loading of modules works:

  public function load($moduleName, $moduleClass, $moduleDir) { //      //        ( PSR-0) $this->classLoader->add($moduleName, dirname($moduleDir)); //    $module = new $moduleClass; $module->setModuleDir($moduleDir); //    /  IoC. //     load //      module.ini $module->load($this->ioc); } 




Create a class Module, which will describe our module.

 namespace Modular; use IoC\Container; use IoC\Assoc\Service; class Module { private $moduleDir; //   . public function load(Container $container) { $this->loadFromFile($container, $this->getModuleDir() . '/module.ini'); } protected function loadFromFile(Container $container, $file) { $module = parse_ini_file($file, true); foreach ($module as $class => $params) { //         //     IoC     Reflection (   ). $interfaces = isset($params['interface']) ? (array)$params['interface'] : array(); //        . unset($params['interface']); //  -   . //  $class        . //      . $serviceAssoc = new Service($class, $params); $container->assoc($serviceAssoc, $interfaces); } } ... } 




Now we will try to create and then expand the module. For simplicity, try to create a notebook. All code can be found here .



Create composer.json:

 { "require":{ "php":">=5.3.0", "elfet/modular":"dev-master" } } 




and run composer install. Now we have a vendor / folder with everything you need.



Create the app / Notepad / folder and start by creating the StorageInterface storage interface:

 namespace Notepad; interface StorageInterface { public function set($key, $value); public function get($key); public function save(); public function load(); } 




and also a simple implementation of FileStorage .

Code
 namespace Notepad; use Notepad\StorageInterface; class FileStorage implements StorageInterface { protected $store = array(); protected $file; public function __construct($file = 'store.json') { $this->file = realpath(__DIR__ . '/../cache/' . $file); } public function set($key, $value) { $this->store[$key] = $value; } public function get($key) { return isset($this->store[$key]) ? $this->store[$key] : null; } public function save() { file_put_contents($this->file, json_encode($this->store)); } public function load() { $content = file_get_contents($this->file); $this->store = (array)json_decode($content); } } 






We describe this class in module.ini :

 [Notepad\FileStorage] interface = Notepad\StorageInterface file = store.json 




Now any class in the constructor (for example, Notepad \ Controller ) that contains the StorageInterface will receive a FileStorage:

 public function __construct(StorageInterface $storage) 




All code of the Notepad module is available here .



Let's try to create a module MyNotepad which will extend the module Notepad. For example, we now want to use DbStorage. Create app / MyNotepad / DbStorage.php and describe it in app / MyNotepad / module.ini:

 [MyNotepad\DbStorage] database = mystore.db 




and add our module to app.ini

 [Notepad] path = Notepad/ [MyNotepad] path = MyNotepad/ 




Now the class Notepad \ Controller will receive an instance of the class MyNotepad \ DbStorage when it is created. This is so simple, without modifying the Notepad module, we have expanded its functionality. On github, you can see how to override other parts of Notepad.



Shortcuts



Source: https://habr.com/ru/post/149435/



All Articles