There are many different engines for
PHP , from fairly simple to very heavy and cumbersome, including almost everything.
But in my opinion the best engines - extensible. And it’s not such that it offers to fasten some to the heap of its capabilities, but one in which there is no way to have its own capabilities, but there are only options to choose from.
Less than six months ago, I wondered about the creation of such an engine. The first thing to write was the bootloader. A kind of pseudo-module (on this later), which loads other modules.
Let's define the structure: let's say we have a mod folder in which the modules are stored.
For example,
/ mod / staticpages / * . According to the loader standard, the module should consist of a configuration file, the main class of the module, and optionally connected libraries.
')
Again, for example, there is a
staticpages module (which is responsible for processing static pages), it will consist of files
manifest.ini and
staticpages.php .
The first is the configuration of the module, and the second is the file with the main class of the module.
To begin, let the config file have the following structure:
[Custom] name = "Static Pages"; author = "ShadowPrince"; devname = "staticpages"; version = "0.1";
When processing this module, its
manifest opens (we will use this term and further), the necessary data is collected from it, the necessary operations are performed.
Add another section to the manifest:
[Module] mainclass = "staticpages.php"; mainclassname = "StaticPages";
Now, when processing this module, we can load (more precisely include) a file with its class and create an instance of it.
Now the algorithm will be something like this:
1. We read all folders
/ mod / .
2. If there is
manifest.ini , continue.
3.
Get the value of
mainclass , include this file.
4. Create an instance of the class named
devname . That is what this field is for.
As a result, we get an object of the
StaticPages class with the name
$ staticpages . The object will be global, for convenient interaction of other modules with it.
Now, in the following code, we can simply and quickly use the capabilities of this module.
But now we run into another problem:
Suppose we have such a query: "
? Ins = staticpage & page = info ", which, in theory, should show a static page with the name
info . But how does the
Static Pages module learn about this, which should be responsible for this?
Of course, you can place a handler in the class constructor, such as if
ins = staticpage , but at that time we don’t know for sure if other modules that are needed for normal operation of
Static Pages were loaded, and in general, is it worth it?
So we need to add another section to the manifest:
[Run] run = "template()"
After loading all the modules, we start the second stage: the launch of the methods.
In this section, the run parameter specifies the class method that needs to be run at this stage. Well, at this stage, all modules will be loaded exactly, and they can easily interact with each other.
Now one more situation - let's say the
pex module (which is responsible for access rights) in this section only starts to load access rights for the current user, and we need to limit the display of the static page, but intervening in the third-party module code is not good at all.
We'll have to introduce another concept, as well as a parameter in the manifest, namely: the queue and require.
The queue, in our case, the queue of modules (which is obvious) to perform some action.
And now - we modify the
[Run] section of the
Static Pages module:
[Run] run = "template()" require = "pex"
When trying to perform the
template () method for our
Static Pages module, the loader will stumble upon the requirement to first perform a similar operation for the
pex module, move our module away in the queue and continue the similar work for the other modules.
Now we can safely wield data obtained by pex after its
Run stage.
One more important thing: check of rigidly installed modules for the operation of our
Static Pages , as well as their versions. Add this department:
[Require] data = "core:0.2, template:0.1, mysql:0:1, lang:0.2";
It can be seen that our module requires a loader version not lower than
0.2 (for this reason it is a “pseudo-module”: it has both a manifest and a version, and it can be accessed like any other module, but it is “hard soldered” into system), as well as a module for working with
MySQL databases , another module
lang (which will be responsible for encoding, date and time format, etc.), as well as a module for page-by-page navigation.
But our “queues” do not have a very good effect on performance, and it will be tedious for developers to point out all the modules that depend on it in one way or another. Therefore, it would be most reasonable to make a “pair of levels” of modular method execution.
These will be new departments in the manifest: first, methods with
[Prev] , already known
[Run] ,
[After] and
[Finish] will be executed
.For example, take the part of the pex module manifest:
[Prev] run = "getGroup()"; require = "auth"; [Run] run = "template()"; [After] run = "nodesContainers()"
First, at the
[Prev] stage, he will receive the rights of the group to which the user belongs, but only after the
auth module has received data about the user himself. Then it will execute the
template () method, in which, for example, it will check whether the current user can view the site (but will also allow other modules to do their work).
And after that, it will check the template on the so-called
nodesContainers , template sections, for access to which certain rights are necessary (after all, in the previous stages, different modules could add such sections and they would remain unworked).
Just do not forget about the library, which may need some modules, add another department:
[Include] dirs = "";
The loader will check this department, and if necessary, include all files from folders.
In the end, in
index.php we will have something like the following:
include "core/lib/module/mod.php";
What do we have in the end?
Almost complete automation of module loading, full system modularity. Working with the database - module
mysql . Working with templates -
template module. The navigation bar is the
speedbar module. Not necessary? Delete the folder.
With the help of convenient template containers, like
<! [Mod = speedbar]> Speedbar here: <! Speedbar> <! [/ Mod = speedbar]> , there is a module - it works and the section is shown. No module - the section is not shown at all.
On this principle, you can build a site of any complexity using existing modules plus written specially. Any module can use any other, and direct execution of the code gives almost 100% flexibility. Need
ajax ? We create a module that will wait until everything is completed and prepared, and then at the last moment will cancel the display of the template and show only what is needed.
Of course, there are some drawbacks to this method: because of the constant construction of the queues, the speed goes down, the modules may not go where they are needed, and the average user who encounters all of this will try to plug a couple of modules and will give up the whole thing.
But from the beginning of my work, I don’t have a wide audience at all, and all that is in use is used only for myself. And while "it" suits me perfectly.
The current model of this method can be viewed on my page (link is in the profile), and the source codes are in the
GitHub repository .
Thank you for your attention, if someone interested the topic, I will write a topic about the transition from theory to practice.