One of the things I’ve always wanted to write, ever since I started writing web backends, is a proper extendable (plugin) framework in PHP. I’ve spent a little time searching for something that would fit my needs, but surprisingly there are very few free, documented plugin frameworks available. Though, I know there are some PHP applications out there that have very good backends (my favorite so far is Gallery 2), but they haven’t released the backend as a standalone package that is documented for others to write their own programs.
A plugin framework is nothing new, as there have been many great minds who have worked on the subject, so I shouldn’t have to reinvent the wheel. I started my search with the well known Eclipse. After delving into the development docs, I found that Eclipse 3 is built on top of the OSGi framework and JPF. So, delving in further, I read the spec on the OSGi framework and, after much thought, I believe it is possible to implement this framework in PHP and, at the same time, break new ground in PHP development by adding features that have never been done before (in PHP).
Goals
Some of the initial goals I had for a proper framework were:
- Universal
- Easy portablility of exisiting code
- Allows plugins lots of power, but in a controllable way
- Plugins should not have to be aware of other plugins it does not deal with
- Fast
- Dynamic
The OSGi framework is a universal framework that allows for dynamic loading and unloading of code. It allows for the easy portability of existing code, and uses namespaces such that there are no name or resource collisions. Each plugin (bundle) has lots of power and flexability (providing a service offers it), but it is all controlled through the framework using services. The OSGi framework does have overhead, and can be quite slow when starting up, but if we can minimize the number of times the framework is started (cacheing, or keeping it running), then the only slow down is the advantages we get from using the framework.
Initial Problems
One of the problems with the OSGi framework, if you start reading it, is that it is designed for Java, and is based on a lot of high-level Java functions. Essentially they are three things that OSGi needs that Java has but PHP does not:
- Class loading
- Namespaces
- Advanced security
The first one (class loading) is not much of an issue since we can import files that have class definitions in them. But what we can not do (easily) is enforce that only class definitions are loaded. Nor should we, as it breaks the very essence of PHP itself. However, we need to keep this in mind as we are implementing the framework for possible pitfalls (and security risks).
The second problem, namespaces, is a very tricky one. One of the original goals of the OSGi framework is to load (or unload) bundles of code such that it does not affect other bundles unexpectedly. (Bundles should not need to be aware of other bundles) Unfortunately when PHP5 was designed, they ended up cutting out namespace support. So what are we left with? Creating separate processes for each bundle isn’t the best answer since it will consume resources like mad, and sharing data would be a pain. However, threads uses very little additional memory, and allows for inter-thread communication easier. Now, unfortunately PHP does not have native thread support, but PECL’s runkit package supports sandboxed threads, and should be perfect for what we need to emulate the way Java implements namespaces. The sandboxing would also allow us to load code into each box without fear of it causing a collision in the main namespace. Also, runkit doesn’t completely isolate each thread from each other, as it provides mechanisms to communicate with the parent. Therefore, when we want to create a new namespace, we create a sandbox thread, initialize it with the environment we want, add functions that allow it to talk with other namespaces, and then load the user code.
The other solution, as I have recently become aware, would be to use the supposed namespace patch for PHP. However, there is apparently some big problems with it and is likely not the best route to take until they are fixed and further integration is done.
Lastly, PHP5 is sorely lacking security features. Sure there is safe mode, but its an all on/off solution and doesn’t allow for the fine grained control that the Java 2 Security Architecture provides (which is optionally required by the OSGi framework). However, I believe that Java’s Security Architecture can be mimicked in PHP5 by using PECL’s intercept package (doc), debug_backtrace(), and code signing. Essentially, before loading any user code, we setup the environment such that we intercept any potentially dangerous system functions and, when called, code will check to see if the calling code (debug_backtrace()) has permission (using code signing) to run the requested function.
Having identified and potentially eliminating the three biggest challenges, one problem remains - the user is required to install/patch at least two extensions to their PHP runtime engine. This is a problem because a) the user might not be able to because they are not admin or are using a pre-compiled package, or b) may conflict with other installed extensions. This problem could be simplified by writing a custom extension for the framework that implements all the desired features needed. But, unfortunately, there is no solution to this problem and is a consequence of using the framework.
Layers
The OSGi framework is defined as a layered structure. We too can also use this structure and add some other layers we need to setup PHP for the framework. The layers for this framework are listed below in an inverted order (bottom to top) and each layer’s code is able to access any other layers functions below it (providing it has the necessary permissions).
PHP5 w/ Extensions
This is a layer that we don’t necessarily need to do anything about. Its listed to hear to identify that PHP5 is required (for its class model, exceptions, etc.). The necessary extensions (runkit and intercept) should also be apart of this layer since they need to be compiled into PHP5 to improve the language enough so that we can do what we need to do.
PHP Environment Control
This layer is a required, simple abstraction layer/class for the compiled in extensions. It provides a standard API for easily accessing the necessary tasks that need to be performed with the compiled extensions.
The purpose of this layer is to abstract the implementation details of the required extensions such that we could potentially use any extension that performed similar tasks (or new language constructs in PHP6), and all we would have to do is modify the abstraction layer. Also, in the future, if a custom extension is written for this framework, it would have this API.
PHP5 Standardized API
This is a completely optional layer, but could bring some structure and help enforce good class-based API design. Essentially, this layer would implement the Java library in PHP. We would not, however, remove existing functions, but duplicate their functionality into a standardized API.
For example:
include('php/language/String');
$comment = new String('This is a coment');
$comment.replace('coment', 'comment');
echo $comment;
There has already been some work on this done with the Japha project as it has implemented the most important parts of the Java Standard API already. However, some modification would be required such that we can now actually implement class (code) loading/namespaces.
PHP5 Security Architecture
This is an optional layer that allows for the locking down of the environment such that untrusted code is allowed to safely execute without fear of causing damage to the system.
This is done by essentially mimicking the Java 2 Security Architecture. Before any user code is loaded, we have a list of all known dangerous system functions, and the required permissions needed in order to access them. Then a parser runs through this list and adds intercepts (using PECL’s intercept package) to each dangerous function. The intercept, when fired, will check if the calling function (look at the stack with debug_backtrace()) has the necessary permissions (using code signing and policies, discussed later) to run the requested function. If it does not, an exception is thrown.
The Java 2 Security Architecture does a good job defining how policies and code signing should work, so I won’t go into detail. But, an authority would need to be established in order to issue these certificates/policies in order for them to be enforceable.
OSGi Security Layer
The OSGi Security Layer is an optional layer that underlies the OSGi Service Platform. The layer is based on the Java 2 security architecture. It provides the infrastructure to deploy and manage applications that must run in finegrained controlled environments.
Essentially, this layer adds extra security features to enhance Java’s own in order to take account the fact that there are code bundles now. When reading this doc, you will notice that it mentions about digital signed jar files a lot. Now, we could either use the PAR Loader to implement this feature, or else we could just have digital signed directories of code that’s optionally installed using PEAR.
OSGi Module Layer
The standard Java platform provides only limited support for packaging, deploying, and validating Java-based applications and components. Because of this, many Java-based projects […] have resorted to creating custom module-oriented layers with specialized class loaders for packaging, deploying, and validating applications and components. The OSGi Framework provides a generic and standardized solution for Java modularization.
This layer defines Bundles, how they are defined and how they work. I won’t go into much detail here since the specification does a great job at it already. Just replace the words “Java” with “PHP”, and its pretty easy to understand and create.
OSGi Life Cycle Layer
The Life Cycle Layer provides an API to control the security and life cycle operations of bundles. […] The Module Layer does not define how a bundle is installed, updated, and uninstalled. [The installation of a bundle can only be performed by another bundle.] These life cycle operations are defined here.
Without going into too much detail, this section defined the API that controls the basics of Bundle control. It also what happens when the framework is started/shutdown.
OSGi Service Layer
The OSGi Service Layer defines a dynamic collaborative model that is highly integrated with the Life Cycle Layer. The service model is a publish, find and bind model. A service is a normal Java object that is registered under one or more Java interfaces with the service registry. Bundles can register services, search for them, or receive notifications when their registration state changes.
This is where our platform gets useful. The OSGi framework is built around services, each bundle having 0 to many services available, which are registered with the service registry defined in this layer.
Services
This layer is what adds the actual value to the framework and its purpose. Although any service could be defined depending the purpose of the application being written, the OSGi framework defines several universal/common services that should be implemented.
-
OSGi Package Admin Service
-
Bundles can export packages to other bundles. This exporting creates a dependency between the bundle exporting a package and the bundle using the package. When the exporting bundle is uninstalled or updated, a decision must be taken regarding any shared packages.
The Package Admin service provides an interface to let the Management Agent make this decision.
-
OSGi Start Level Service
-
This specification describes how to enable a Management Agent to control the relative starting and stopping order of bundles in an OSGi Service Platform.
The Start Level service assigns each bundle a start level. The Management Agent can modify the start levels for bundles and set the active start level of the Framework, which will start and stop the appropriate bundles. Only bundles that have a start level less or equal to this active start level must be active.
The purpose of the Start Level service is to allow the Management Agent to control, in detail, what bundles will be started and stopped and when this occurs.
-
OSGi Conditional Permission Admin Service
-
A key aspect of this security management API is the real time management of the permissions. This enables management applications to control the permissions of other applications with immediate effect; no restart is required.
-
OSGi Permission Admin Service
-
In the Framework, a bundle can have a single set of permissions. These permissions are used to verify that a bundle is authorized to execute privileged code. For example, a FilePermission defines what files can be used and in what way.
The policy of providing the permissions to the bundle should be delegated to a Management Agent. For this reason, the Framework provides the Permission Admin service so that a Management Agent can administrate the permissions of a bundle and provide defaults for all bundles.
-
OSGi URL Handlers Service
-
This specification is necessary because the standard Java mechanisms for extending the URL class with new schemes and different content types is not compatible with the dynamic aspects of an OSGi Service Platform. The registration of a new scheme or content type is a one time only action in Java, and once registered, a scheme or content type can never be revoked. This singleton approach to registration makes the provided mechanism impossible to use by different, independent bundles. Therefore, it is necessary for OSGi Framework implementations to hide this mechanism and provide an alternative mechanism that can be used.
-
OSGi Log Service
-
The Log Service provides a general purpose message logger for the OSGi Service Platform. It consists of two services, one for logging information and another for retrieving current or previously recorded log information.
-
OSGi Http Service
-
An OSGi Service Platform normally provides users with access to services on the Internet and other networks. This access allows users to remotely retrieve information from, and send control to, services in an OSGi Service Platform using a standard web browser.
I think this service could be the money maker for this framework. Essentially, you could create this service by modifying Nanoweb to work as the HTTP server, then you could run this framework standalone (without Apache) and get an increase in PHP performance and control.
-
OSGi Configuration Admin Service
-
The Configuration Admin service is an important aspect of the deployment of an OSGi Service Platform. It allows an Operator to set the configuration information of deployed bundles.
Configuration is the process of defining the configuration data of bundles and assuring that those bundles receive that data when they are active in the OSGi Service Platform.
-
OSGi Preferences Service
-
Many bundles need to save some data persistently–in other words, the data is required to survive the stopping and restarting of the bundle, Framework and OSGi Service Platform. In some cases, the data is specific to a particular user. Some data is not specific to a user, which we call system data.
-
OSGi User Admin Service
-
OSGi Service Platforms are often used in places where end users initiate actions. These kinds of actions inevitably create a need for authenticating the initiator. Authenticating can be done in many different ways, including with passwords, one-time token cards, bio-metrics, and certificates. Once the initiator is authenticated, it is necessary to verify that this principal is authorized to perform the requested action. This authorization can only be decided by the operator of the OSGi environment, and thus requires administration. The User Admin service provides this type of functionality.
Another important feature of this framework as it allows for a single, powerful, and standardized authentication system for the framework across multiple different application.
-
OSGi Event Admin Service
-
The Event Admin service provides an inter-bundle communication mechanism. It is based on a event publish and subscribe model, popular in many message based systems.
-
OSGi Service Tracker Service
-
The specification defines a utility class, ServiceTracker, that makes tracking the registration, modification, and unregistration of services much easier. A ServiceTracker class can be customized by implementing the interface or by sub-classing the ServiceTracker class.
-
OSGi Declarative Services
- This service is a bit complicated in its definition, but basically provides a method of adding services that do not have to worry about services it depends on being active, and will only cause the framework to only load services when needed (reducing footprint and startup time).
There are various other services that the OSGi specification specifies. The ones not listed here are not likely needed for a PHP environment. (Unless say it was the only application running on a dedicated hardware device)
Usage
This is how I envision the framework being use; The application using the framework is designed to be run standalone and runs like a daemon process. At startup, it loads all the neccessary services (including the HTTP service) and then the application service. The application service controls a blog that is displayed (and receives feedback) from the HTTP Service. It also provides an admin interface for allowing the loading of plugins. These plugins are loaded/unloaded/updated at runtime and the framework never has to shutdown. Because the PHP code is loaded into memory and stays, the running of PHP code is fast and results come back quick.
Conclusion
Although a framework like this would be a tremendous undertaking (not including any applications built on top of it), I believe that this would get a lot of media attention and cause several preexisiting apps to be ported to the framework and, consequently, allow for the easy mashup of various services. And why not? Most pre-exisiting applications would only require a manifest and class loader file, and they could be run by the application without worry of namespace collisions. This could also breathe some new life into PHP development.
Now, having said all of this, none of this has been tested. This is purely a concept idea and may actually not be possible. My biggest concerns are, in fact, the two required extensions and if they can be loaded together and are able to perform a good enough job for what is needed. Having said that, a custom extension could (and eventually should) be written to handle the necessary limitations of PHP.
Now, I know a lot of people oppose the whole “PHP should not try to be like Java” bit, but Java has some really nice features, and combining those with PHP’s really nice features, we could create something better then the both of them. Just my opinion on it.
If anyone is interested in working on a framework like this, please feel free to contact me or leave a message in the comments.
February 6th, 2006 at 9:29 am
Hello Gary,
First of all, excuse my poor english. I love your solution. Have you taken a look to this site http://phpnamespaces.org/wiki/
There is a huge comunity waiting for a builting solution to namespaces on PHP. Do you think that your solution ussing PECL’s runkit package is better than keep working with that namespaces patch until them fortunatly release a solution.
I am in the same trouble with my framework, but i am afraid to develop a solution that can be deprecated in some months.
February 9th, 2006 at 4:15 am
Hello Gary,
I’m also working on a PHP Plugin Framework in my diploma thesis. I started with the OSGi specification and then I took a look at some OSGi implementation in the Java Language (Eclipse, Oscar, Knopflerfish) to see how things has been done there.
I think, it is possible to implement the OSGi specification in PHP, but as you mentioned in your concept PHP doesn’t have somethings that OSGi needs. I found the same problems as you:
- namespaces: I think it is a serious problem
- class loading: I think class unloading is a problem
- Security: I was not engaged in it - it’s not so important for my thesis
March 1st, 2006 at 2:41 pm
I’d recommend not to waste any of your time with reinventing the wheel/copy concepts from Java _until_ the Zend framework has arrived. You’ll bite your ass if you see companies / developers spending time rather on some kind of “official” framework/architecture than “Yet another (bloated) PHP Framework”.
May 2nd, 2006 at 12:02 am
Why not marry the OSGi framework as it is (in Java) with a PHP engine? The power of PHP is clearly in generating web page, something Java is really lousy at, despite the enormous investments. Creating a bridge between PHP and Java allows you to eat your cake and have it too. The OSGi framework is more than flexible enough to detect JARs that contain PHP code and link them appropriately. It is relativey straightforward to communicate between PHP bundles and Java bundles because of the service concept. You can easily generate dynamic service stubs with Java that then call PHP code, and vice versa.
You can create/use a PHP engine written in Java or you can link it to a native engine. This engine can be written as an OSGi bundle.
Going this route will allow each language do what it is good at and making both more valuable. Using the OSGi framework as is will make it possible to have something running in days because there are so many standard frameworks available.
Just my 2cts worth.
Kind regards,
Peter Kriens (OSGi spec editor)
May 2nd, 2006 at 11:04 pm
Peter: Holy crap, that’s almost genius!
Actually, I took a look into linking PHP into Java. I didn’t really find much in the way of a Java written PHP engines, but integrating the native PHP engine using, say, the PHP / Java Bridge could make this work fairly easily. You could easily write a wrapper in Java for the engine, and a PHP class that abstracts the communication back to Java, and get access to all the functionality of the OSGi framework.
The biggest problem I see with this, however, is that most PHP programmers would scoff at the idea of letting Java run their PHP environments. Apache and PHP is like sacred for most, and wouldn’t see or want to bother with getting something like this working.
Very interesting idea, something I may even pursue one day.
July 7th, 2007 at 7:45 am
Hi
I am Lucy, I have found your website while searching for some info at Google. Your site has helped me in a big way.
Bye