PHP Frameworks Day
PHP Frameworks day is an event that took place in Kiev, Ukraine, last October. It is an event with talks about different frameworks.
I only became aware of it now thanks to PHP Quick Fix stream of news that Chris Cornutt (AKA enygma) of PHPDeveloper.org fame puts together. Thanks Chris.
Why All PHP Frameworks Suck
Rasmus Lerdorf, PHP创始人, 应邀在PHP框架日会议上发表演讲。他主要讲说了最新的PHP研发，但对我来说最为有趣的一块是问答部分。
除此之外，有人问Rasmus他怎么看PHP框架的。这是一个直接问及他观点的问题, Rasmus也给出了直接的答案 (视频 31分 47秒): "它们 (指PHP框架) 都是垃圾!"
可能一个演讲嘉宾去PHP框架大会上讲说PHP框架都是垃圾, 会让人诧异, 但听众却对这种答案却很满意。不管怎样吧，Rasmus还是细讲了他所要表达的具体意思。
1. Frameworks Execute The Same Code Repeatedly Without Need
A more specific complaint was that the solutions that frameworks offer lead to executing needless PHP code repeatedly in every HTTP request. The example that Rasmus gave is that in every request frameworks check the database type the application is using to load the respective database access class. Since the database type does not change after an application is deployed, he sees this as a waste.
While I agree with Rasmus, I think this example is not very compelling because checking the configuration to decide which database access class to load takes a very small fraction of time, especially when compared for instance with executing database queries, which usually take many milliseconds and sometimes take a few seconds to run.
A better example of this problem is when frameworks need to read configuration files to define load the actual configuration values.
Often frameworks read configuration from INI files. PHP has built-in functions to load and parse INI files. Despite you can do it all with a single function, reading a INI file and parse it takes some time that is usually way more than checking the parsed configuration values.
If your framework reads and parses configuration values from files in other formats that PHP does not have built-in support, like for instance YAML or XML, things get worse because the frameworks have to do the parsing in pure PHP code. That is much slower than the C code of the PHP engine that parses INI files.
A better alternative is to have configuration values defined in PHP script files. Just put the configuration values in PHP scripts that assign the values to variables.
When you use a PHP caching extension, PHP scripts are only compiled once. On a second run, PHP scripts compiled into opcodes are loaded from RAM. That is much faster than loading configuration from files.
2. Frameworks Require Too Many Interdependent Classes
While this is true to a certain degree, I have seen efforts from certain framework developers to reduce the dependencies between distinct components. Still there are often dependencies between many frameworks classes that sometimes do not aggregate anything to an application with specific needs.
To address this problem some developers need to change the frameworks to strip the needless parts that add overhead. This causes a maintenance nightmare because they need to do that every time they want to upgrade to a newer version of a framework they started to adapt for their needs.
Rasmus suggests using frameworks optimized for specific purposes to avoid this problem. He recommends using for instance Wordpress or Drupal if you just want to publish a blog.
Alternatively Rasmus suggests that frameworks provide a means to let the developers push to production just a small subset of the components that are needed in each application.
This solution is too general. Rasmus did not get to the way certain frameworks implement things and so he did not comment on why certain frameworks need so many components.
For instance many frameworks rely on runtime ORMs (Object Relational Mapping). These are components that let developers define how to query databases treating information as objects, rather than tables of records.
Object orientation is fine for abstracting problems and encapsulating solutions into classes of objects, but the way certain ORMs work adds too much needless overhead.
The developer has to write code to dynamically specify the class variables (tables fields), condition clauses, object relationtionships (table joins), etc... to compose the actual query at runtime. This adds a lot of overhead because the queries that are executed are the same on every request, apart from some parameter values that may vary.
There is a better solution that avoids this overhead. Instead of dynamically composing queries at runtime, just have a separate tool that generates PHP code for the ORM classes. The generated classes already have the compiled SQL queries to execute without further overhead at runtime.
I have been using this approach since 2002 when I developed a ORM tool named Metastorage. It does exactly what I described above. I define in project file the objects, variables, relationships and functions that I need to apply on objects.
Metastorage processes my objects definitions and generates ORM classes that execute the necessary queries at runtime just by calling the classes functions. No query building is done at runtime.
3. Needlessly Complicated Solutions
One thing that Rasmus did not mention directly is about the complicated solutions that frameworks tend to push.
That is the case for instance of application version migrations. Some frameworks have copied the concept of migrations from Ruby On Rails. This means that you have to write code to change your database schema between different application versions.
This is another thing that Metastorage addresses in a more efficient and less painful way for developers. Metastorage generates database table schema definitions in a separate file from my object definitions. It generates an installation class that installs the database tables on the first time.
If I change the object definitions, the installation class can also upgrade the schema with the newer definitions without destroying any data already inserted in the database tables.
This certainly makes development much faster and application upgrades less error prone because the tool always generates correct code to upgrade the database schema. When you write migrations code by hand, you may make mistakes that make you spend more time and effort to fix.
4. Duplicating the Web Server Functionality
For instance, routing is the processes of assigning some code (a controller) to handle requests with different URL patterns. Many frameworks push applications to use the front controller pattern. The front controller analyzes the request URL and load a specific controller to actually handle the request.
The matter here is that the Web server already does this. It can match the request URL against configuration (for instance of mod_rewrite or similar) and execute the appropriate PHP script.
When you make PHP handle the routing process, you are adding needless overhead to perform a task that is the same for every request with the same URL pattern. This falls into Rasmus complaint of frameworks that execute the same code repeatedly to the reach the same outcome.
This seems to be yet another bad influence that PHP frameworks got from Ruby On Rails and Java. With those languages the Web server forwards the request to an application server.
PHP does not need to work this way because it always runs integrated with the Web server, so there is no point duplicating the Web server functionality in a way that is slower and adds more overhead.
在本次会议上, Rasmus也回答了我认为值得评鉴的其他有趣问题, 。
Dropping APC in Favour of Zend Opcode Cache
This is a topic that we have discussed several times in the Lately in PHP podcast. Rasmus explained that PHP needed to adopt one opcode cache that would follow the latest PHP developments on every new release.
There are several opcode caches. Rasmus decided to give up on APC in favour of Zend's solution because it is more mature and faster. That required Zend to make their solution Open Source.
Curiously the maintainer of the now official PHP opcode cache is Dmitry Stogov. He was the original developer of the Turck MMCache that Zend hired to work on their on cache extension some years ago.
All is well when it ends well. Too bad that PHP took all this time to have an official caching extension. The lack of an official extension made PHP look bad in many benchmarks that in the past favored other languages.
Compiling PHP into Binary Code
While this is true, Rasmus is just considering solutions that merely compile PHP into opcodes and encrypt the result. This is a solution that is really not so hard to break by hackers.
However there are better solutions that consist in compiling the result code into native assembly machine code. While it is always possible to decompile machine code, it is much hard to reverse engineer it to PHP code that is useful enough to be understood by people that wants to steal work or change it in some useful way.
One concern that many developers that look for copy protection solutions of PHP code, is that somebody with access to the servers where the code is installed, changes the code easily.
I have seen many times developers that work for customers and those customers just go there and change their code without the knowledge of the developers. This creates maintenance headaches. Sometimes customers complain about code that is not working well because in reality they changed the code. So a solution to make it harder to view or change installed code would help.
For those cases, nowadays developers can minimize that problem by creating PHAR archives. These are binary archives that contain one or more PHP scripts. While PHAR archives are not really a copy protection solution, at least they would make it harder for customers that want to poke on the developers code.
$ Dollar Sign in PHP variables
When asked about why variables start with the $ sign, he explained that was meant to be able to insert variables inside literal string values, so a mark would need to be used to distinguish what is a variable from the rest of the string.
Since he wanted the variables to look the same inside and outside a string, he has chosen the $ sign to start variables, inspired in the solution that Perl also adopted.
Node.js and Non-Blocking I/O
无论如何，异步编程 (非IO阻塞)也就是Node.js有一种遗憾，写起来感觉很不舒服, 因为他要解决每个闹心的回调函数。
Unicode and JIT on PHP 7
When asked about the plans for future PHP versions, Rasmus commented that he learned from the PHP 6 Unicode support failure that it was a goal that was too ambitious. So he expects that PHP evolves in smaller hops.
Two goals he thinks are too ambitious but will eventually be implemented maybe in PHP 7 are the native support to Unicode based on a simpler approach than ICU, and a JIT compilation engine probably based on Google V8 or Facebook HHVM.
Rasmus interview was very interesting because it makes us reflect on the way we are doing things in PHP that may be less than ideal, especially when you use general purpose frameworks.
Whether you agree or disagree with the points of view, post a comment here to tell what you think about these topics.