PHP Component and Library API Design Overview

January 18th, 2011 § 9 comments

There’s been lots of change in the PHP community over the past few years. PHP now has namespaces. More PHP developers are using an IDE. More PHP developers are pulling inspiration from the Java, C#/.NET, and Ruby communities. And even more PHP developers are embracing the object-oriented and, ironically, the functional nature (closures) of PHP. All these changes make for interesting code. What has also happened is that better and more readable code is being produced by this ever growing PHP community. It’s been a long time since “PHP application” meant a series of transaction scripts as a mix of SQL, CSS, JS, with some PHP sprinkled in, and a couple of few classes for good measure. Of course, that still exists, but you no longer need to go to the ends of the earth to find non-spaghetti code that is understandable within a few minutes.

For the most part, all of these changes are good changes. The number of good/senior/expert level PHP developers is ever increasing and there are more and more “enterprise grade” frameworks and libraries that are being produced. That said, with all of these new changes, the one area which is still fairly inconsistent from project to project is the naming conventions that are employed inside PHP 5.3 project that utilize namespaces. This article will attempt to describe what an API is, how names and object-oriented features affect an API, and how various decisions affect the consumers of a particular API is.

What Is An API?

Before we jump into naming, it’s important to have a common understanding of the actual problem area. When we talk about names, we are really talking about the API. An API is a particular set of rules and specifications that a developer can follow to access and make use of the services and resources provided by another particular software program, component or library. Put another way, it is an interface between various software pieces and facilitates their interaction, similar to the way the user interface facilitates interaction between humans and computers.

For PHP 4 / procedural based libraries, the API is defined by the functions that are declared for usage in that library. It is further described by the global names and global state that the library utilizes to do its job. Typically, API’s based on purely function based libraries are far simpler to understand.

Object-oriented API’s are a bit more complex. When you build an object-oriented library or component, you are typically designing two API’s at the same time, whether or not you know it. This is the nature of object-oriented languages when you employ the use of abstract classes and interfaces in your design.

The first API, the more common of the two, I call the Consumption API. This is the API that answers the question: “how do people consume this thing.” The answer to this question is generally situated around the great majority of use cases that were identified by the author of the software component/library. In PHP, consumption might look like this:

As you can see, no declarative code was required to fulfill the most common use case that was identified as a need for this component’s existence. The above API is defined by the totality of all the public (concrete) classes, their public properties and public methods. By examining these elements, a good API design should allow a developer to deduce how the component works without examining any documentation. When that is possible, the API has become the documentation as well as the “story” behind how the component/library is to be used.

Not all use cases are accounted for in generic components and generic libraries. As developers, we attempt to create generic libraries and components that will solve the majority of problems of the majority of the community. We cannot envision all use cases or even edge cases behind a particular component. That said though doesn’t means that the outlying use cases are unimportant or should be unaccounted for. These use cases are handled by the secondary API: the “Extension API”.

The Extension API answers the question: “since this component does 90% of what I want, how can I extend it to fulfill the last few of my needs.” Clearly, it makes sense to leverage tools that do most of what you need especially if they can be extended in ways that are outside of the out-of-the-box feature-set. Object-oriented/class based code is particularly well suited to extension through the principle of overriding polymorphism.

The primary tool behind overriding polymorphism is method overriding. For this to be possible, base types, or the types that are shipped with the component/library you are extending, will be overridden to fulfill this new behavior that is your specialized use case. Consider the following code example:

As you can see here, we’ve extended the functionality of the base adapter from the shipped component/library with our own functionality. This is possible since the base adapter tucked away the business logic we needed to alter inside a protected method. This is what allows us to rely on overriding polymorphism to extend code to suit more specific needs. This “Extension API” can therefore be defined by the totality of all protected members of a class: methods and properties that can be utilized in child classes. These protected methods are not all that important or even useful in the documented and de-facto use cases of a component, but become extremely important when extending.

API Philosophy

It’s hard to quantify importance of any one aspect of a codebase’s API over another without first talking about the general philosophy. In the land of a 1000 frameworks and libraries, being well written and poorly written divides the great majority of them. Of what is left of the (generally regarded) well written ones, philosophy divides the rest.

There exist two common philosophical “goals” that most libraries/components generally subscribe to that, depending on your perspective, might be contradictory. For arguments sake, let’s assume that each is as important as the other. The first: “easy to use”. A component’s like-ability by developers is greatly determined by how easy something is to use, if it’s intuitive, if it’s fulfills the majority of one’s needs. The other: “easy to extend”. The majority of the time, a component is written for some well known use cases. Generally, that will suite the majority of the needs of any one developer, but there are always some unknown use cases. A components ability to be able to deliver a mostly working solution while allowing the developer to extend it for the unknown is what determined how easy it is to extend said component.

More often than not, ease of use and extensibility live at two ends of the spectrum. Things that are easy to use are generally hard to extend, and things that are simple to extend are generally harder to use. This is the case because to accommodate one usually comes at the expense of the other.

Getting back to philosophy and this example at hand, both ease of use and extensibility are both equally important. The goal, in terms of API design, is to be able to accommodate each equally and strike a balance between the two so that each goal is represented in the API.

Basic Tips And Tricks For Better APIs

The tips and tricks for building better component API’s could get fairly long, so this article will attempt to cover some of the more “basic” ideas.

Adopt A Common Namespace & Class Naming Scheme

While it is true that the PHP platform has no built-in packaging, or file based import mechanism… the PHP autoloader with the help of some common conventions can get you 99% of the way there. Large projects like Zend Framework, Symfony, PHPUnit, and PEAR have all settled on a pretty simple and common naming scheme based on the PEAR naming standards. By utilizing this naming scheme, your code will be instantly familiar to developers who already have knowledge of this scheme in other projects. The benefit here is that developers will know exactly where to find classes inside the filesystem.

Avoid Doing Too Much In the Constructor

There’s lots of places on the web that discuss this, so I’ll link to them here and not go into too much detail. I’ve seen it called a “unified constructor”, but that’s not what we are talking about here, or at least, that is not the goal. The goal is to allow the consumer to give as much or as little information about the identity of the object at instantiation time. The common signature that I like for this is the following:

Generally, the call to setOptions() will in turn call various setters if they exist. What is important is that at construction/instantiation time a consumer is not required to fulfill all of the classes requirements. Why is this important? It reverses order in which dependencies are required to be interacted with. Lets examine this in code:

The difference is that in Example 1, even though our target use case is handled by class Foo, we are forced to interact with the dependencies first. Conversely, examples 2 and 3 show that our target object Foo is created up front, and dependencies are handled after instantiation. If code clarity is a goal, reading the code top down in example 2 and 3 makes more sense than in example 1 since the API has allowed the developer to code his use case in a top-down or story-like code block. Why do I like this pattern of usage? Simple: it highlights PHP’s loose nature and flexibility in it’s use case… but mostly because it’s more readable.

Avoid final And private

This one speaks to extensibility. Unless you are attempting to restrict a user from utilizing some kind of use case, there is little gain in marking members as final or private. Sooner or later, someone somewhere will need to override a method you’ve implemented for some obscure use case. A better approach is to provide them with a codebase that will meet most of their needs and can be extended to fulfill the rest if they are outside the original scope. That way, they are not forced to patch your codebase.

Summary

This is by far not an exhaustive list. As more of the larger projects move to using namespaces, closures and the other PHP 5.3 features, we’ll start to see a few more best-practices emerge as they relate to API design. In the mean time, this overview will serve as a springboard for a few discussions on API design moving forward with ZF2 and PHP 5.3 component development that is currently on-going.

Tagged , , ,

  • http://blog.jorygeerts.com Jory Geerts

    Nice article. But in all honesty, I find example 2 to be far les readable then example 1.
    Where can I find that I can/have to provide indexes ‘a’, ‘b’ and ‘c’ in order for it to work properly? Its not clear from the code, so I’ll need to read the (api)documentation.
    And if you want to avoid it becoming an unreadable block of code, you’ll still end up creating the $options array first, meaning you’ve defeated [this/the] purpose.

    The upside is, all these issues can be handled very nicely by a dependancy injection system. :)

  • Frank Kleine

    I think your constructor recommendations are quite the opposite to what you propose regarding good APIs. If an API should be self-documenting and readable, only constructor example 1 fulfills this condition, as this clearly states the dependencies required for Foo, while example 2 hides the dependencies, and example 3 looks like as if the dependencies are not required dependencies. When ease of use is another API goal, example 1 is IMHO better at this point. First, because the API clearly states the requirements to get a working instance of FOO, and second that in example 2 or 3 you will only find the problem of missing dependencies when you run the code. Or you need good class comments for Foo, but this violates your rule that the API should be self documenting. :)

  • http://ralphschindler.com/ Ralph Schindler

    Looking back over this, I’ll admit that using the options constructor was not a basic example; I forgot how complex a subject it was – I should have left this one out.

    In general, I agree with you both (Jory and Frank). Unfortunately, there are a few other concepts I left out of this article (that will be explained in a future article). This concept of the options constructor is not a very good pattern to apply to all classes within a component as it suffers from the exact points you both mention in your comments. This pattern is particularly useful when it is applied to the components primary and identifiable entry point. In ZF1, you would have seen this as, for example, Zend_Form. The Zend_Form class was the most identifiable entry point for the Zend_Form component which consisted of the family of Zend_Form_* classes. That said, this pattern really is not about a classes API, but more about a component’s or a library’s API.

    That said, dependencies should be injected. The primary verbiage in the __constructor is “$options”. This means that you can optionally provide them in the constructor. Some might be dependencies, some might simply be behavior modifying flags. Either way, in this pattern, they are optional because their existence does not affect the identity of the object. If a required dependency is to be considered as part of the identity of an object, then it should be injected into the constructor, with type hinting.

    Either way, I won’t alter the post above, but I will admit the example was less than ideal since it was too abstract, lacked context, and is (in my mind) supported by other concepts not mentioned here. Stay tuned! :)

  • Pingback: Ralph Schindler’s Blog: PHP Component and Library API Design Overview | Development Blog With Code Updates : Developercast.com

  • http://rob.olmos.name/ Rob Olmos

    As with any API, I hope there will also be discussions (and future articles) on PHP exceptions. Although I see that Zend Framework has been improving by better utilizing exceptions in recent component additions, I’ve had recent frustrations with some of the Amazon PHP libraries, two of them involving the Zend Framework as detailed in my blog post that I would like to share:

    http://rob.olmos.name/2010/12/lack-of-exceptions-in-php-libraries/

  • Pingback: PHP scripts? | SiteMembershipScript Blog

  • Heikki Naski

    Excellent article, as usual.

    I agree that there’s some ambiguousness about the “requirements” and “dependencies” on the constructor example. One might mistake those for hard requirements which you absolutely need before instantiating the object, instead of needing those only for a particular operation with the class.

    To add some context to the example, the class might have a method which implies the dependencies needed. For instance, calculateWidthOfLetters(). The class probably does something else which doesn’t need those dependencies and this would then additionally imply that you do not need those. For instance, hasNoLetters().

    I do not agree with always using the protected modifier for members. I think the subject was brought up in the movie The Unforgiven as the cowboys contemplated OOD:
    “Declaring a class member protected is a hell of a thing. Take away all the design freedom a man’s got, and all the design freedom he’s ever gonna have.”
    After declaring something protected and someone overrides it in a subclass, you really can’t change that member anymore or any other member it uses, since it might cause subtle bugs in the extending classes that are hard to find.

  • http://theamiableapi.com/ Ferenc Mihaly

    Great post, Ralph. I’m very interested in API design in general (just started blogging about it) and in how various languages and programming environments encourage or prevent API design best practices. I’m wondering if you know any resources specifically about API design in PHP? Thanks.

  • Fernandos

    So much fuss for a Makefile like depedancy management?
    It’s not important to make dependancy injection confortable to the developer by introducing complex chains of code that need to be parsed, but good enough code to avoid that dependancy chains.

    Easier said then coded, but that’s a deficit in php that you’re trying to cure. Providing class indexing and injection using autoload appears like a quirk to me. Contrary to what my tone might sound like, I’m very open to discussion and admit when I’m wrong. That said, I think php wether needs a fix or it’s going to suffer in popularity and loose important market share in about 5-10 years. What I think php tries to introduce with autoloading is actually package management, not class management. That’s why I think it doesn’t matter too much if you use autoloading or not, because it’s a quirk.

    Regards
    Fernandos

What's this?

You are currently reading PHP Component and Library API Design Overview at Ralph Schindler.

meta