Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
Let us begin putting things into perspective, assess and discuss in concrete terms, what it means for phpMyAdmin to migrate towards MVC, it also includes a survey of progress recently initiated in that direction.
For the philosophy behind the MVC architecture and basic tenets of it, there are ample resources available on web. Still for sake of continuity, let's mention the basic tenets we should aim to achieve, not strictly same as some ideal water tight MVC architecture, but something like following:
- A user request goes to a Controller, naming some specific Action for it to perform (which may involve some manipulation of data via the Model, resulting in some result data), then Controller will send the data to the View (for rendering of presentation) and will get presentable data in return, which is then returned to the user.
- So ultimately Controller will act as the middleman, interfacing with Model and View.
- This in effect separates the business logic(Model) from the presentation(View).
About using frameworks
Though we have had discussion about framework acquaintance, this assessment so far involves no use of frameworks and for most part the MVC migration is assessed to result from progressive refactoring of codebase. It appears that even when using a MVC framework, similar codebase overhauls will be required, but probably in a more rigid and rapid way. So due to constraints on resources and an old codebase, we can possibly aim for a rather gradual movement covering most MVC principles in due time.
About built in basic mechanisms that a framework would have facilitated for us, the problem is that codebase is not at all ready to use those mechanisms, so as it becomes mature, we will make those mechanisms ourselves as per requirement, as has already been done for templates, controllers, DIC and in some part for autoloading.
Templates (First step)
Our sixteen year old codebase has its own customs and ways, so we appreciate that we have HTML generating functions, controller logic, business logic, all mashed together in different proportions. Yet, there are many bright areas already with OOP where we have considerable to remarkable clarity. So first step would be to fetch the HTML part out and put it into a template based structure. This step should necessarily leave us with libraries/*.lib.php, libraries/*.class.php files and *.php files devoid of HTML generation or HTML/PHP mix.
Although looking at the history of discussions on templates, we have been ambivalent about its feasibility and desirability, only until recently, when in a GSoC project, we had a template system introduced to PMA, where one can define a template file in templates/ folder with .phtml extension.
Templates can be used as described in https://wiki.phpmyadmin.net/pma/Templates
To look for the examples of how it is being done, we have good number of PRs:
- Refactoring tbl_view.lib and add PMA\Template: https://github.com/phpmyadmin/phpmyadmin/pull/1642
- Updating PMA\Template class: https://github.com/phpmyadmin/phpmyadmin/pull/1671
- Complete templates for table related scripts: https://github.com/phpmyadmin/phpmyadmin/pull/1700
- Manage logo display in navigation panel, with a template: https://github.com/phpmyadmin/phpmyadmin/pull/1733
OOP and Controllers (Second step)
Previous step aimed at broadly achieving separation of presentation code from rest of the logical codebase. And this rest of codebase, now free from a lot of HTML generating functions, can undergo an evolution under OOP principles.
It will necessarily involve conversion of libraries/*.lib.php files into classes with meaningful restructuring. This step would involve codebase research to evolve a good plan, though an initial proposal is already underway as a GSoC project https://wiki.phpmyadmin.net/pma/GSoC_2015_Ideas_List#Codebase_Improvements:_OOP_and_refactoring
Now we come to the Controller part, interestingly Controllers have already been introduced into PMA with a GSoC project, so while making transitions from libraries/*.lib.php to OOP, we can directly move towards making Controllers. Now in this step, how is a controller class different from a normal class?
- A Controller class would implement the abstract class libraries/controllers/Controller.class.php or maybe its subclasses like TableController.class.php, to which I propose addition of an abstract method indexAction(), which would mean a default page/action for each Controller in addition to other actions.
- Controllers will basically aim at action-wise encapsulation of only that logic which, just uses appropriate core functionality to do a requested operation, then fetches result data and passes the same to templates to render HTML. All other logic which makes sense from a Model point of view or which can be reused by many other Controllers, may be moved to some core file, or may be for present we need not worry much about this as the line between Controllers and Model in our case can be planned to be a bit liberal, at least for the beginning.
- Controllers will follow a component wise folder hierarchy, inheritance structure introducing reusability, and will use namespaces following PSR-4.
- And indeed the action functions defined in a controller should correlate with the actions asked for in user requests from the UI, so that will require moving towards single point of entry and hence true controllers will materialize only at later stages when we do further refining after this first go
So, this step will in effect eliminate *.lib.php files. Also, we already have many libraries/*.class.php files, so they also need to be converted to controllers where appropriate.
As for *.php files, they will now only contain logic to instantiate the required Controller and pass appropriate dependencies to it.
Dependency Injection Container
In our codebase right now, we define dependencies whereever we need them in a script. But as we move to OOP and particularly Controllers, things begin to appear cleaner and then we have an opportunity to make it appear ultra clean, to achieve an MVC edge ;), the basic aim is to achieve loose coupling of objects. In concrete terms, when instantiating a requested controller in a *.php file, it should not be done with a 'new Controller()', but by just asking the DI Container for an object of particular controller, like Container.get('Controller'), such that Container handles plugging of dependencies and building of Controller, so basically:
- the Controller gets all its dependencies as parameters of its constructor.
- or Controller should be able to locate the rest of dependencies by specifying just a name identifier, using a service locator(also provided by the DI Container). Service locating pattern may make sense for dependencies like REQUEST/SESSION/GLOBAL variables or something else maybe?
With this we also get a single place to manipulate all dependencies when making changes to code.
An example from tbl_gis_visualization.php on how things in *.php files will broadly look, once Controllers and DI is achieved:
/* contents from from tbl_gis_visualization.php */ namespace PMA; $container = DI\Container::getDefaultContainer(); $container->factory('PMA\Controllers\Table\TableGisVisualizationController'); $container->alias('TableGisVisualizationController', 'PMA\Controllers\Table\TableGisVisualizationController'); /* Define dependencies for the concerned controller */ $dependency_definitions = array( "sql_query" => &$GLOBALS['sql_query'], "url_params" => &$GLOBALS['url_params'], "goto" => PMA_Util::getScriptNameForOption($GLOBALS['cfg']['DefaultTabDatabase'], 'database'), "back" => 'sql.php', "visualizationSettings" => array() ); /** @var Controllers\Table\TableGisVisualizationController $controller */ $controller = $container->get('TableGisVisualizationController', $dependency_definitions); $controller->indexAction();
More examples of how it is being done right now, are:
Moving forward when we reach an evolved stage, this existing DI Container will find more space to evolve into a true mechanism for building objects plugging in dependencies dynamically, which may be defined in separate XML files.
Routing and single point to entry
To achieve URLs of a form like index.php?controller=something&action=something, exact form may vary but purpose is to make index.php as the only interface for the user to interact with PMA. So, once we have full Controller coverage of codebase and all *.php files contain just controller instantiation code, then we can remove all *.php files and just keep the index.php while implementing a router mechanism to locate which Controller to call and which Action to perform, this will be facilitated with a fully functional DIC.
Autoloading of classes
To remove lot of require statements in every class and provide for automatic class loading whenever class is used in a script. Currently in another GSoC project, we have had an autoloader introduced to PMA, see libraries/sql-parser/ClassLoader.php. Also see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader-examples.md
So we need to introduce this autoloading provision for Controllers as well.
Further progress and limitations
So far, this document takes just an overview of the broad patterns which can be followed and it lacks thorough study of our codebase with all its intricacies, which will surely pose roadblocks to smooth application of methods considered here. And indeed, as we set on to do things in practice, we will keep knowing the limitations that are hidden for now and we will know how to remove them. So further improvements/opinions and research are very welcome. Same is the case with further work towards isolating Models.