Tony Marston's Blog About software development, PHP and OOP

4+ Reasons Why All PHP Frameworks Suck - Except RADICORE

Posted on 17th February 2014 by Tony Marston
Introduction
1. Frameworks Execute The Same Code Repeatedly Without Need
2. Frameworks Require Too Many Interdependent Classes
3. Needlessly Complicated Solutions
4. Duplicating the Web Server Functionality
5. Frameworks are general purpose and are not optimised for everybody's needs
Comments

Introduction

In his article 4 Reasons Why All PHP Frameworks Suck? Manuel Lemos expands on the comments made by Rasmus Lerdorf at the PHP Frameworks Day in 2013. It may be that many or even most of the PHP frameworks out there exhibit these problems, but not all do. In this article I will explain why the RADICORE framework does not have these problems.

1. Frameworks Execute The Same Code Repeatedly Without Need

Rasmus gave an example where in every request the framework checks the database type that the application is using in order 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. Manuel Lemos pointed out that the actual problem may be the way in which the configuration options are defined. Some frameworks use XML or YAML files, or even INI files, but these files need to be read in and parsed each time. Manuel's advice is as follows:

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.

What method does RADICORE use? It has a single PHP script called CONFIG.INC which loads the configuration options into variables.

Although my framework supports several different database engines, the code to actually decide which one to load at runtime is incredibly simple and therefore not much of an overhead. The code exists in two places. In my CONFIG.INC file I have:

$dbms = 'mysql';  // database engine is MySQL
//$dbms = 'pgsql';   // database engine is Postgres
//$dbms = 'oracle';  // database engine is Oracle
//$dbms = 'sqlsrv';  // database engine is SQL Server

The code to load the designated class and instantiate the Data Access Object is also quite straightforward:

require_once "dml.$dbms.class.inc";
$dao = new $dbms($args);

Could it be any simpler than that? Not only that, but unlike most frameworks I have seen where a connection to the database is opened up before control is passed to the Business layer component, in the RADICORE framework the connection is only made by the Business layer component after the data has been validated just before it actually needs to communicate with the database. This is known as the "Just In Time" (JIT) approach instead of the "Just In Case" (JIC) approach. This is more efficient as it does not open up any database connections that it does not actually use.

2. Frameworks Require Too Many Interdependent Classes

Another point that Rasmus mentions is that sometimes you need only specific parts of a framework, but since the framework classes have too many dependencies between each other, you need to load a large number of classes even when you use simple features of the framework. This is mainly because the framework provides too many options and does not provide the means to easily turn off the options you don't want. It may also be because individual options are provided in multiple classes with shared dependencies with other options, which leads to a spaghetti-like structure which is difficult to unravel.

Any application that you build which has separate components or modules to perform separate tasks will have dependencies. It must have otherwise it simply wouldn't work. How modules interact and the degree of mutual interdependence between modules is called coupling. If two modules interact they can either be loosely coupled (which is supposed to be good) or tightly coupled (which is supposed to be bad). For example, if module 'A' calls module 'B' then 'A' is dependent on 'B'. If 'A' can only be used with 'B' and 'B' can only be called from 'A' then they are tightly coupled, and this is not good. If 'A' can be made to call a selection of modules, not just 'B', then this is better. If 'B' can be called by a selection of modules, not just 'A', then this is better.

The RADICORE framework implements a version of the Model-View-Controller pattern in which the Model is split into two - one containing business logic and a second containing data access logic. There is a separate Model class for each table in the database, but a single Data Access class (with a separate class for each database engine). The coupling between these components is extremely loose by virtue of the fact that they are interchangeable by design:

The RADICORE framework does not have a complex class hierarchy with multiple dependencies. Indeed the structure of the framework is so simple it can be shown in a single diagram. How many frameworks do you know that can reveal their entire structure in a single diagram?

The RADICORE framework provides four subsystems with optional features which can be turned on or off very easily.

Unlike other frameworks RADICORE does not provide options which you can only turn on by writing your own code to access the relevant libraries.

3. Needlessly Complicated Solutions

The specific example given by Manuel Lemos concerned database migrations. RADICORE does not have the same problems encountered in other frameworks simply because it was specifically designed for building database applications. The starting point is always the database design, and after the database schema has been built the structure is imported into the Data Dictionary, then exported to produce one class file and one structure file for each database table. If the database structure is ever changed I simply save the SQL script that I used to modify my copy of the database and include this script in the next download. Changing the software to deal with database changes is also very easy - simply change the database schema, import in the Data Dictionary, then export to recreate the structure file (but not the class file which may have been amended). I do not have to amend any code following a change in the database structure unless that change affects any business rules.

A common problem I have within other frameworks is that they suffer greatly from Object Oriented Overkill in which they try to follow the Single Responsibility Principle by splitting large classes into smaller classes each of which have a separate responsibility. Unfortunately once they start this splitting operation they simply do not know when to stop and keep going until there is nothing left but a huge number of classes each with a single method containing a single line of code. I have seen examples where an entity's properties and methods have been split out into separate classes when in actual fact they all belong in a single class. If I can construct a web page using no more than 4 objects - Model, View, Controller and Data Access Object - then how come other frameworks require 30 or more? The worst example I have ever seen was with the Zend framework which accessed 100 files and instantiated 100 classes just to build the index page. That is what I call serious over-engineering!

This is because too many developers choose to follow the KICK principle (Keep It Complex, Knucklehead) instead of the KISS principle (Keep It Simple, Stupid).

A typical example of this is the use of an Object Relational Mapper (ORM). As I say in my article Object Relational Mappers are EVIL I consider that Object Relational Mappers are used by Retards Who Know Everything Except SQL.

There are too many programmers out there who follow the mantra "everything is an object" without thinking of the consequences. Where does it say that the properties of objects have to be objects themselves? Instead of using objects where they provide a visible benefit over their non-OO alternatives they use objects at every conceivable opportunity whether they provide benefits or not, with the usual result of "NOT". Anybody with an ounce of intelligence knows that an SQL query is nothing more than a string, and that the complex SELECT query is nothing more than a series of substrings. It may come as a surprise to some of you knuckleheads out there, but PHP has a perfectly good set of string manipulation functions, so why do you insist on making life unnecessarily complicated by creating a brand new set of objects and methods just to manipulate a query string?

Another artificial rule which I have heard repeated far too often is that a class should never have more than 'N' methods, and each method should have no more than 'N' lines of code - where 'N' is a totally arbitrary number that differs from one person to the next. Their reasoning is that each method should have no more lines than can be viewed on their monitor without scrolling. This logic is flawed for two reasons:

  1. If you do not have the mental capacity to retain in your mind more information than can fit into a single screen then you plainly do not have the mental capacity to be a programmer. Not only do I have to know about the lines of code which I can see on the current screen, but I also have to keep in mind, perhaps in broad detail only, all the functions, methods, classes and components that I cannot currently see.
  2. The golden rule of encapsulation is that once you have identified an entity - something which has properties and methods - you put all the properties and all the methods into a single class. If you break it up into smaller classes you are violating encapsulation and at the same time making it harder for the next person who reads your code to find out which class does what.

In the RADICORE framework, for example, I have a single abstract table class from which all the individual table classes inherit the vast majority of their code. This abstract class contains hundreds of methods and thousands of lines of code. I have been told by more than one person that this is too big. I reject this argument simply because their definition of "too big" is wrong. In the universe which I inhabit I work with the following definitions:

My abstract table class has all the properties and methods that it needs - no more and no less - therefore it is the perfect size. I have identified an entity - a database table - along with all the properties and methods which I may need to view or maintain the contents of a database table. The abstract class contains everything that I need except the table name and the table structure, and these essential details are provided in the constructor of the concrete table class like so:

<?php
require_once 'std.table.class.inc';
class #tablename# extends Default_Table
{
    // ****************************************************************************
    // class constructor
    // ****************************************************************************
    function __construct ()
    {
        // save directory name of current script
        $this->dirname     = dirname(__file__);
        
        $this->dbname      = '#dbname#';
        $this->tablename   = '#tablename#';
        
        // call this method to get original field specifications
        // (note that they may be modified at runtime)
        $this->fieldspec = $this->getFieldSpec_original();
        
    } // __construct
    
// ****************************************************************************
} // end class
// ****************************************************************************
?>

Apart from the Zend framework which requires 100 classes just to build an index page, I encountered another glaring example of over-engineering in the SwiftMailer library which uses 100+ classes spread over 20+ directories just to send an email!!! WTF!!! I have dealt with emails myself, and all I see is a single "email" entity with the following methods:

Apart from the "send it" method which may require a choice of transport mechanisms I simply cannot understand how anyone with a modicum of common sense could take that small list of methods on a simple email entity and turn it into a mind-boggling 100 classes.

People have told me that my approach to OO programming is too simple. I submit that the problem is actually the reverse - it is their approach which is far too complicated. They seem to think that any idiot can write simple code, and to prove their mental superiority they have to write code which is more complicated, more obfuscated, with multiple layers of indirection, something which only a genius like them can understand. I'm sorry, but if you write code that only a genius can understand then your code is incapable of being read, understood and maintained by the average programmer. The true mark of genius is to write code that anyone can understand.

4. Duplicating the Web Server Functionality

This is a rant against the Front Controller pattern. In his article The no-framework PHP MVC framework Rasmus had this to say:

Just make sure you avoid the temptation of creating a single monolithic controller. A web application by its very nature is a series of small discrete requests. If you send all of your requests through a single controller on a single machine you have just defeated this very important architecture. Discreteness gives you scalability and modularity. You can break large problems up into a series of very small and modular solutions and you can deploy these across as many servers as you like.

What he is saying, in summary, is that you shouldn't waste your time in writing code which duplicates what the web server already does. Each user transaction should have its own Page Controller script, and the URL can take you straight to this script without going through an intermediate router object. In this case the web server is the router, so writing your own router would be a duplication of effort and therefore a waste of time.

A colleague (now an ex-colleague) once told me All the big boys use a front controller, so if you want to be considered as one of the big boys you should use it too. That was a pathetic argument then, and it is still just as pathetic now. There is nothing I can do with a front controller that I cannot do without one, so I consider a front controller to be a total waste of time and effort, so I choose not to waste my time with one. I have written an application which contains over 2,000 user transactions, and every transaction has its own URL which points straight at the component script which performs that transaction. This works perfectly without the need of a separate router object, so a router is not "necessary", and a front controller is not "necessary".

People say that the benefit of a front controller is that you can have all your initialisation code in a single place which you can execute before you pass control to your page controller. Such people must be totally unaware of one of the fundamental principles of computer programming, commonly known as the DRY principle, which is that of code duplication. Instead of having separate copies of the code in multiple places you create a single copy in a shared library which you can then call from as many places as you like. Each of my page controllers starts with the command initSession(), so the same copy of the standard initialisation code is always executed before the page controller does the rest of its stuff. Anyone who says that the existence of multiple calls to initSession() is itself a duplication of code should stand at the back of the room and wait for their brain cells to develop into something more than a gelatinous mass which does nothing but fill the gap between their ears.

5. Frameworks are general purpose and are not optimised for everybody's needs

This was mentioned in one of Manuel's answers, but I'd like to make it a separate and distinct point.

Several frameworks out there try to be "all things to all men" and attempt to provide a general purpose solution for any type of application which you wish to build, but end up being a "jack of all trades but master of none". This means that you may have to spend valuable time in identifying those components that you need or removing those components that you don't. Rasmus suggests using a framework which is optimised for a specific purpose, such as Wordpress for a blog or Drupal for a content management system (CMS). RADICORE is not a general-purpose one-size-fits-all framework as it was designed specifically as a Rapid Application Development (RAD) framework for building back-end administrative web applications or enterprise applications.

If you don't know what a Rapid Application Development environment is then ask yourself a simple question - starting with nothing more than a database schema how long will it take you to develop and run a series of fully functional transactions which enable you to browse, create, read, update, delete and search each table in that database? RADICORE can do that within 5 minutes without having to write a single line of code - no PHP, no HTML and no SQL. Can your current tool or methodology match that? How much code do you have to write in your favourite framework before you can get a form to run? For an example of how convoluted some people make it take a look at Easy Admin Interfaces with Active Admin in Rails or CRUD (Create Read Update Delete) in a Laravel App.

If you don't know what a back-end administrative application is then you must have either led a sheltered life or have only been involved in simple toy websites. Anybody who has had real experience with commercial applications will be able to tell you that the front-end website is what members of the public see while the back-end is reserved for the site administrators and members of staff. They each have a separate URL and a separate "look and feel". Back-end administrative applications usually require features that are unknown in front-end websites, such as Role Based Access Control (RBAC), Audit Logging, and perhaps an Activity-based Workflow system. Your typical script kiddie will barely be able to describe what these mean let alone design and build the code to implement them.

A typical example of a business area which requires a back-end application which is totally different from and in addition to the front-end is eCommerce which allows members of the public to purchase a company's products via the internet instead of having to visit a physical store. The front-end is nothing more than a glorified Order Entry system which makes use of fancy HTML, CSS and JavaScript/Ajax to present a shop window for the visitor. Its emphasis is in looking sexy, slick and unique, but its functionality is limited to little more than "Display Products, Take Payments, Accept Orders". Taking orders on its own is not the end of the story - far from it - as the business then needs to ship the products to the customer as quickly and efficiently as possible. This back-end processing can be known by several different names, such as Order Processing, Order Fulfilment, Supply Chain Management or Enterprise Resource Planning. It will typically have software components which deal with the following areas:

A typical back-end application can be up to 100 times bigger than its puny front-end. I myself have used RADICORE to build a standard back-end eCommerce package which is used by numerous companies, each with their own bespoke front-end, and while the back-end application has over 2,000 screens the front-end websites can barely reach an average of 20. While the screens in the front-end have to be unique and sexy the screens in the back-end can be more utilitarian as they are more concerned with allowing members of staff to view data in rows and columns, and to update data according to certain business rules such as progressing an order through the stages of authorisation, picking all the way to shipping. There may be other tasks such as inventory replenishment which requires sending purchase orders to suppliers.

But what use is a framework which can only be used for back-end applications I hear you say. Three words - 3 Tier Architecture. In this architecture the application components are split into three separate tiers or layers: the Presentation layer (User Interface or UI), the Business layer, and the Data Access layer. While the RADICORE framework has its own Presentation layer which is constructed from a library of pre-defined XSL stylesheets, it is a very simple process to create a separate and bespoke Presentation layer for a front-end website, yet share the existing Business and Data Access layer components that were created in the back-end. This greatly reduces the amount of effort required to build a front-end website as all the complex work - the database design, data access and business rule processing - has been taken care of in the back-end application. All the front-end script kiddie has to do is knock together a few web pages with the latest sexy HTML, CSS and JavaScript, then call the existing Business layer components to do the important stuff. How simple is that?


counter