![](http://habrastorage.org/getpro/habr/avatars/f31/761/31b/f3176131b2068ef6cce39d01b30d8608.jpg)
Functions Strike Back
In this topic I want to talk about the approach that I have been operating for several years.
I must warn you right away if you are not a fan of OOP, huge constructions and monstrous class diagrams, do not read.
In short, the essence of the concept is the transfer of part of the unix way to programming in PHP.
Specifically, the concepts of simple programs that perform one function.
Working on many systems and making various OOP monsters using frameworks, I constantly felt discomfort and a feeling of excessive verbosity.
Hash interface, class Abstract_Hash, a descendant of Hash_MD5 ...
In which there will be a single-line wrapper for a standard function.
Isn't it too much for simple hashing?
They usually object to me that this improves the readability, reusability and maintability of the code.
But imagine that we need to replace a specific implementation mechanism, according to the Strategy pattern ?
We come to either abstract factories or search-n-replace in code.
This does not draw on KISS , and only exacerbates the problem.
And if the components are tightly coupled?
I saw projects in which the Zend Framework and Kohana were mixed, on the basis of the "codec reunion". And by the way, it was also offered for money.
Simple functions came to my aid. Ordinary functions that have one purpose.
Such as: or (I chose simpler examples, so as not to burden the article) Functions are named through an agreement of the form (in one implementation there can be several functions, for example, for encryption, there are four of them: encryption, decryption, initialization and installation of the vector). They are stored in a structure like: where "Generate / Entropy" is a kind of "namespace".
They are called through the singleton class, let's call it Code. The Code class has one method - E [xecute]. In this case, I sacrificed readability in favor of brevity, because There are a lot of calls to this method.
Ideally, make a short alias, like _ ($ Args), but gettext has already taken that name.
Method E is declared as
The list that goes further is a description of the capabilities of a real class, but there will be no direct link to avoid accusations of PR.
From the configuration of your framework or application, it can take data on default implementations, eliminating the need to specify the last argument, and giving you the ability to control the code from the config, for example, replacing UID generation methods on the fly or replacing algorithms with different accuracy / performance ratios .
From my practice, I recall deploying an application written for PHP 5.2 on a server with PHP 5.1, in which there is no native JSON implementation.
Replacing the native with your bike took less than a minute.
You can also implement smart sampling, which will replace specific implementations, depending on the time of day, current load, client geography, here the limitation is only your imagination.
It is not superfluous to mention fault tolerance, because a class can iterate over drivers (specific implementations), catch exceptions and adjust its configuration.
In such a scheme, caching of the results of the execution of any code is easily implemented.
To do this, you just need to add to the configuration an indication of which namespace / function can be cached and for how long.
You can implement this with a separate method E [xecute] C [ached], so as not to burden the logic of the main method.
A simple RPC server, a block in the configuration that will indicate exactly what and where to execute, and you get the opportunity to transfer some harsh number-breaking operation to the remote server without changing the code.
To screw the simplest queue server is a task that everyone can do.
We call now, it is executed when it is convenient for us.
Great for functionality that returns nothing.
Only recording - deleting functions come to mind.
Your system may have its own area that needs micro-regulation.
Here you can add profiling, logging and much more.
Functions are designed as simple as possible, in the spirit of unix, with the expectation of sharing.
For example, F_RSS2_Import accepts exclusively strings and allows the developer himself to choose which mechanism for receiving the RSS feed.
Simple functions are convenient to debug, they work quickly and combine perfectly.
Unfortunately, many frameworks sin by mixing data acquisition and processing, this leads to the inversion of abstraction and verbosity.
The new implementation does not require changes in the program code - just configuring and copying the file to the right place. A kind of Convention over naming | configuration.
This does not always work, but most functions can be made independent of the host system, without external calls and bindings.
This opens up great opportunities for the exchange of code, solutions and best practices. By the way, to integrate some code, you just need to wrap it in a function with a name by agreement.
At one time, I managed to transfer a collection of about 300 functions to a system developed by another person, porting my class to it.
In half an hour, I managed to do the same with the Zend Framework.
I note that using foreign classes is not so convenient - it usually leads to bloatware, violation of conventions, etc.
This is a topic for holivar, but it is obvious that simple classes are more expensive.
Functions are included from external files, the global scope is clogged.
I did not notice the impact on performance, but the sediment remains.
At the moment, I look closely at the anonymous functions stored inside the Code singleton.
More precisely, its absence.
An associative array is not the most annotated data structure. Saved docblocks, but the deprivation of auto-completion in the IDE can scare.
Although, in my experience, I did not notice any inconvenience.
Not as big as the classes, but, nevertheless, it is.
The issue is controversial, proper formatting and proper use minimize shock from multiple calls of the same method.
In real projects, there are not so many calls.
OOP for the sake of OOP has become so familiar that the approach I described is unlikely to gain popularity.
Within the framework of the article, I indicated simple examples, I will give a list of applications from real projects: conversion (Arabic-Roman, SI, XML, RSS, APML), input processing, output processing (emoticons, smart tags, dynamic tags in template engines), generation (random numbers , uids, passwords), routing in frameworks, slots, receiving data (database abstraction), conditions, message transports, etc.
The purpose of the topic is only to talk about this method of organizing code, which helps me a lot.
This is not a crusade against the PLO, not the ultimate truth and not a guide to action (a reservation is needed here, after my first topic, in which I was accused of personally misleading billions of juniors).
If someone is interested in the implementation details, please comment.
I must warn you right away if you are not a fan of OOP, huge constructions and monstrous class diagrams, do not read.
In short, the essence of the concept is the transfer of part of the unix way to programming in PHP.
Specifically, the concepts of simple programs that perform one function.
The background is as follows.
Working on many systems and making various OOP monsters using frameworks, I constantly felt discomfort and a feeling of excessive verbosity.
Hash interface, class Abstract_Hash, a descendant of Hash_MD5 ...
In which there will be a single-line wrapper for a standard function.
Isn't it too much for simple hashing?
They usually object to me that this improves the readability, reusability and maintability of the code.
But imagine that we need to replace a specific implementation mechanism, according to the Strategy pattern ?
We come to either abstract factories or search-n-replace in code.
This does not draw on KISS , and only exacerbates the problem.
And if the components are tightly coupled?
I saw projects in which the Zend Framework and Kohana were mixed, on the basis of the "codec reunion". And by the way, it was also offered for money.
The entry was delayed, get down to business.
Simple functions came to my aid. Ordinary functions that have one purpose.
Such as: or (I chose simpler examples, so as not to burden the article) Functions are named through an agreement of the form (in one implementation there can be several functions, for example, for encryption, there are four of them: encryption, decryption, initialization and installation of the vector). They are stored in a structure like: where "Generate / Entropy" is a kind of "namespace".
function F_Standart_Random($Args)
{
return rand($Args['Min'],$Args['Max']);
}
function F_MonteCarlo_Random($Args)
{
return mt_rand($Args['Min'], $Args['Max']);
}
F_ИмяРеализации_Функция($Args)
/Drivers/Generate/Entropy/MonteCarlo.php
The fun begins now.
They are called through the singleton class, let's call it Code. The Code class has one method - E [xecute]. In this case, I sacrificed readability in favor of brevity, because There are a lot of calls to this method.
Ideally, make a short alias, like _ ($ Args), but gettext has already taken that name.
Method E is declared as
public static function E ($Namespace, $Function, $Args, $Driver = 'Default')
What does he do or can do?
The list that goes further is a description of the capabilities of a real class, but there will be no direct link to avoid accusations of PR.
Configuration is everything
From the configuration of your framework or application, it can take data on default implementations, eliminating the need to specify the last argument, and giving you the ability to control the code from the config, for example, replacing UID generation methods on the fly or replacing algorithms with different accuracy / performance ratios .
From my practice, I recall deploying an application written for PHP 5.2 on a server with PHP 5.1, in which there is no native JSON implementation.
Replacing the native with your bike took less than a minute.
You can also implement smart sampling, which will replace specific implementations, depending on the time of day, current load, client geography, here the limitation is only your imagination.
It is not superfluous to mention fault tolerance, because a class can iterate over drivers (specific implementations), catch exceptions and adjust its configuration.
Cache it
In such a scheme, caching of the results of the execution of any code is easily implemented.
To do this, you just need to add to the configuration an indication of which namespace / function can be cached and for how long.
You can implement this with a separate method E [xecute] C [ached], so as not to burden the logic of the main method.
Remote function call
A simple RPC server, a block in the configuration that will indicate exactly what and where to execute, and you get the opportunity to transfer some harsh number-breaking operation to the remote server without changing the code.
Deferred calls
To screw the simplest queue server is a task that everyone can do.
We call now, it is executed when it is convenient for us.
Great for functionality that returns nothing.
Feature Level ACLs
Only recording - deleting functions come to mind.
Your system may have its own area that needs micro-regulation.
Here you can add profiling, logging and much more.
Advantages of the approach, in addition to those already indicated:
Minimalism
Functions are designed as simple as possible, in the spirit of unix, with the expectation of sharing.
For example, F_RSS2_Import accepts exclusively strings and allows the developer himself to choose which mechanism for receiving the RSS feed.
Simple functions are convenient to debug, they work quickly and combine perfectly.
Unfortunately, many frameworks sin by mixing data acquisition and processing, this leads to the inversion of abstraction and verbosity.
Adding functionality through the file system.
The new implementation does not require changes in the program code - just configuring and copying the file to the right place. A kind of Convention over naming | configuration.
Independence.
This does not always work, but most functions can be made independent of the host system, without external calls and bindings.
This opens up great opportunities for the exchange of code, solutions and best practices. By the way, to integrate some code, you just need to wrap it in a function with a name by agreement.
At one time, I managed to transfer a collection of about 300 functions to a system developed by another person, porting my class to it.
In half an hour, I managed to do the same with the Zend Framework.
I note that using foreign classes is not so convenient - it usually leads to bloatware, violation of conventions, etc.
Speed and resources.
This is a topic for holivar, but it is obvious that simple classes are more expensive.
Cons, they are not enough.
Globality
Functions are included from external files, the global scope is clogged.
I did not notice the impact on performance, but the sediment remains.
At the moment, I look closely at the anonymous functions stored inside the Code singleton.
Description of the format of the arguments.
More precisely, its absence.
An associative array is not the most annotated data structure. Saved docblocks, but the deprivation of auto-completion in the IDE can scare.
Although, in my experience, I did not notice any inconvenience.
Overhead is present.
Not as big as the classes, but, nevertheless, it is.
Code readability.
The issue is controversial, proper formatting and proper use minimize shock from multiple calls of the same method.
In real projects, there are not so many calls.
Object Oriented Masturbation.
OOP for the sake of OOP has become so familiar that the approach I described is unlikely to gain popularity.
Within the framework of the article, I indicated simple examples, I will give a list of applications from real projects: conversion (Arabic-Roman, SI, XML, RSS, APML), input processing, output processing (emoticons, smart tags, dynamic tags in template engines), generation (random numbers , uids, passwords), routing in frameworks, slots, receiving data (database abstraction), conditions, message transports, etc.
The purpose of the topic is only to talk about this method of organizing code, which helps me a lot.
This is not a crusade against the PLO, not the ultimate truth and not a guide to action (a reservation is needed here, after my first topic, in which I was accused of personally misleading billions of juniors).
If someone is interested in the implementation details, please comment.