Introduction
Zend Framework 2 is an open source Framework for the development of professional web applications for PHP Version 5.3. and higher. It is operated on the server and is primarily used to produce dynamic web contents or conduct transactions such as purchasing a product in an online shop. In this context, Zend Framework 2 can support the development of any type of (web )application because it provides universally applicable solutions that can be used in e commerce, content, community or SaaS applications equally well. Zend Framework is essentially being developed by the Zend Company, which also gave the Framework its name and provides it with a solid (financial) basis, not least also because Zend itself is also involved in the development of the PHP programming language. However, in addition to Zend, a number of prestigious companies also support Zend Framework and are interested in its long-term success. Among them are also Microsoft and IBM. All of the above provides security for the selection of Zend Framework and also makes its selection a good decision as the basis for one’s own application from an economic point of view.
Zend Framework first stable version (1.0) appeared on 30/06/2007. It has since made a lasting impression within the PHP development community and made the PHP language acceptable for enterprise development. PHP and Zend Framework provides a balanced ratio of lightness and professionalism other platform will find difficult to beat.
Zend Framework is already more than 6 years old. Required additions to the glut of releases from first preview to version 1.12 could not fit within the old code base. Hence Zend Framework 2.0, and a new beginning using the latest advances in PHP.
Zend Framework 2 marks the next milestone in the evolution of the PHP web frameworks. It marks an evolutionary milestone in PHP too. Like Java and Ruby, a good programming language alone is not enough for success on a broad front. Frameworks symbiotic with the programming language, such as Struts, Spring, Rails and especially Zend Framework, significantly reduce initial development effort and long-term maintenance established.
Who is this book for?
It is a challenge to write a technical book which finds a balance between theory and practice, allowing both novices and professionals to get the best out of it. I gladly accepted this task, but left myself an escape hatch. Later explanation or references are supplied to overcome any of my straying in a forest of details. No big promises, but I plan to continually revise text and examples: so keep your purchase up to date!
A further challenge is how to address both developers new to Zend Framework 2.0 and “old hands”. For those familiar with Version 1 I frequently refer to this predecessor when appropriate, without going into excessive detail. That might also perhaps help novices, because in this manner they would get a better feeling for why Version 2 is necessary.
I presume that you have basic knowledge of PHP. You do not have to be a PHP expert, particularly because many “native” PHP functions became obsolete when one uses Framework techniques. For example, Session Management, which maps in an object-oriented manner and abstracts some of the low-level functionality. Hence, if you are accustomed to PHP syntax, have a basic understanding of the operating principles of PHP applications, and are familiar with common functions of the language core, you are well prepared. Bear in mind you will also need to use a PHP language references, readily available on-line.
You & I
Hi, I am Michael. I hope that you won’t object if I occasionally use contractions and the less formal "you" instead of "one" in my explanations. That makes writing it easier for me and ensures a less formal atmosphere.
Structure of this book
This book is not meant to be a compendium. More a pragmatic and practice-oriented introduction to basic concepts and practical work with Zend Framework 2. In one’s progress as a developer, the official documentation serves well as a compendium for experienced developers, but is not really appropriate for use whilst becoming familiar with the subject. Instead, the book's objective is to serve as an (indispensible) reference work for further detailed questions achieving required basic understanding.
It is structured such that you can read it from the beginning to the end, and that is what you should do. We will begin with an overview of the Framework, first looking at essential concepts and ideas forming its essence and differentiating it from its predecessors. On the way, we will repeatedly also look to the left and right, becoming familiar with the Framework conditions, for example ideas and discussions behind ZF2's development. Then elucidate the Framework’s most important components and relationships; peek at how a HTPP request is processed; write our first bit of code; and an excursion into the Framework environment. We will examine the most important third-party modules. Modules are a large part of the magic of ZF2. Modules can greatly accelerate and simplify application development. Expected a large number of high-quality modules!
Last but not least, there is an additional, intensive practice section in the form of a “developer’s diary”. We will develop a web application together, one intended for subsequent use by a business enterprise. So try hard to thoroughly grasp concepts — to prepare yourself for “hands-on” work!
Repetitions
You will soon realise that I frequently explain contexts several times throughout this book. My excuse? Old Romans knew: Repetitio mater studiorum est – Repetition is the mother of learning. Thus, if you happen to find a place in the book where you think, “I already knew all that!”, just be glad that you have learned so much and continue reading. Beware: repetitions have different contexts, and different tricks to assist the reader to acquire memorable, recallable, knowledge.
How you can best work with this book
Programming (or handling a programming framework) is best learned when you become active yourself. Indeed, particularly the first part of the book up to the practice section is already helpful even without an opened IDE, but you will have the greatest possible learning success if you reproduce some of the lines of code or write some yourself. In the ideal case, you have a system with a debugger at hand, with which you can follow the Framework's mode of operation step by step. Some knowledge of Git and a GitHub account would not hurt anything either, but are not essential.
Found a bug?
Have you found a bug in the text or code? Please feel free to file a bug ticket in the bug tracker. I am thankful for your feedback and support.
Conventions used in this book
Code examples
The listings in this book have Syntax Highlighting, wherever possible, but do not always conform to a coding standard; this serves to make them more legible. PHP code is introduced by a <?php, and a // [..] in the respective listing indicates excluded code fragments.
Many listings have a link to GitHub, where they can be downloaded simply be adopted with "Copy & Paste". In this manner, code examples can easily be re-enacted on your own system.
Command line
When a command has to be executed on the command line, this is symbolised by a preceding dollar sign ($) . The visual feedback of the command is indicated by a preceding "greater than sign" (>). Example:
$ phpunit --version
> PHPUnit 3.6.12 by Sebastian Bergmann.
So, that is enough of the foreword, let’s get down to work!
Zend Framework 2 - An overview
Before we begin to immerse ourselves in the details of Zend Framework 2 in the further course of the book, we initially want to get an overview and to elucidate some of the core aspects and thoughts underlying the Framework. What are the Framework’s main characteristics?
How Zend Framework 2 is being developed
Zend Framework 2 is open source. Initially, this means that anyone can examine the source code and use it for his or her own purposes. Zend Framework 2 is being developed under the "New BSD License". Whereas under the original "BSD License" it was still necessary to refer to the use of a library or a framework under "BSD Licence", this is not necessary for Zend Framework 2; the so-called "Marketing Clause" does not exist in the "New BSD License". We do not want to drift off into software licensing law, but do want to say at this point that, with reference to the law, that the Framework is making things easy for us because the "New BSD License" indeed belongs to the so-called free software licenses, which have practically no limitations or directives for the use of the code.
Whereas Zend Framework 1 still used SVN for the code administration, the project team for the new release decided to administer the Framework code on GitHub. GitHub supports the joint, distributed work on the code base very well, especially since many programmers are now involved in the development and are spread across the globe. Thus, Rob Allen resides in England, whereas Matthew Weier O'Phinney, Project Head for Zend Framework 2 lives in the USA, and Ben Scholzen, in Germany. The above-mentioned programmers are all so-called "code contributors", i.e. they have already made a significant contribution to the Framework and thus have a special status in the team. They decisively shape the Frameworks structure and design. Some of the programmers are directly employed at Zend, for example Matthew Weier O'Phinney; others are freelancers or have other working conditions which allow them to collaborate on Zend Framework during or after their working hours. Thus, it is a colourful group of good people. Zend Framework’s component-orientation allows the use of so-called "Component Maintainers", who are responsible for a specific Framework component, and they themselves or jointly with other programmers control the fate of a certain component.
The PSR-2 Coding Standard
The code of the Framework itself is being developed across all components under consideration of the PSR-2 Coding Standards. The idea of the "PHP Specification Request", "PSR" for short, was inspired by the Java Specification Request. Using this procedure, new Java standards are defined and extensions of the Java programming language or the Java runtime environment are jointly developed and are agreed upon by all manufacturers. This procedure has many advantages for the application developer because it makes the application proper much more portable and manufacturer-independent. Thus, it is possible in a (more or less) simple manner, for example, to change the provider of one’s own application server technology.
The "PHP Specification Requests" are based on a similar idea—they should, in particular, ensure that software components made by different manufacturers and used in different PHP frameworks are compatible to one another and can be used in combination. In contrast to "JSR", the "PSR" procedure is relative new. To date only four specifications have been agreed upon.
PSR-0: Defines the coherency between PHP namespaces and the organisation of PHP files in a file system in order to make the autoloading of classes, which should also be component- and manufacturer-independent, as simple as possible.
PSR-1 / PSR-1 basic: A new common coding standard.
PSR-2: An extension of the PSR-1 Coding Standard.
PSR-3: A common logger interface.
A primary focus of Zend Framework 2, as we will repeatedly see in the course of this book, is on reusable code modules. Compliance with the PSR standards is a great help in this context.
Known problems
As is the case for every large piece of software, the Framework is not completely free of errors. In a consequent manner, github issues is used for tracking. Thus, if you discover a problem, it is worthwhile to initially look there to see whether the error is already known. If not, you can open a thread there.
Module system
The module system is the major achievement of Version 2. Matthew Weier O'Phinney stated this extremely clearly in the mailing list:
"Modules are perhaps the most important new development in ZF2."
The Framework's Module System was completely reworked for Version 2. Even though it was indeed already possible to organise one’s own code in modules, those modules were never really independently usable nor could they be transferred to other applications. The module system of Version 1 was thus restricted to the advantages of a better code organisation within a self-contained application. The result is that we now probably have thousands of implementations for user management, or similar functions, which are generally applicable and not restricted to a specific application.
One is accustomed to adding functional extensions in the form of plugins or extensions to applications such as WordPress, Drupal or Magento. Even Symfony 2 - a popular, alternative web framework — has already had a module concept with its bundle system, which makes it, for example, possible to integrate the functionality of a content management system (CMS) into one’s own application. And now Zend Framework has also included this extension option in its new version. Let’s take a concrete example again: An application, which was developed on the basis of Zend Framework 2, subsequently additionally requires the functionality of a blog. Anyone who has worked with Wordpress & Co. knows how comprehensive the requirements for a modern blog system have meanwhile become. It quickly becomes clear that it is not a good idea to now develop one’s own blogging software.
Instead, it appears appropriate to use one of the available free or commercial blogging systems, which however due to its concept can only be set up in parallel to one’s own application. This has a number of disadvantages: For example, it is not easily possible to use the logging system of one’s own application without further ado; nor is this possible for the caching layer. The blog’s data are located in another database, and if we want to display the last 3 blog post teasers on our applications homepage, we have implement this tie-in via one of the blog’s APIs, possibly over an RSS feed or something similar (or mess around in the database of a third party application). Naturally, the administrator accounts which allow our employees to administer customer master data do not exist in the blog system, and we should not even mention Single-Sign-On. We have to simulate our corporate design in the blog’s template system because layout, markup and styles are not readily available there. Any future design adaptations will also always have to be reconstructed there. Our application’s build scripts cannot be used for the blog; in contrast, the release processes must be adapted so that we can somehow also include the third party blog system. Thus, with this approach we skid directly into the complexity of Enterprise Application Integration (EAI) and Service Oriented Architecture (SOA), and in this manner we create a colourful bouquet of new problems and challenges for ourselves. If instead a blog module for Zend Framework 2 were available to use — one which would seamlessly integrate itself in the existing authentication, build & release, caching, logging and design implementations — everything would be much more simple, indeed nearly trivial. And exactly this train of thought is the core idea of the module system.
A Zend Framework module brings everything with it that is required for its operation. This includes not only the appropriate PHP code, but also the HTML templates, CSS, JavaScript code, images, etc., so that a Module is a truly self-contained package. A good module can be readily integrated in a Zend Framework 2 application and … simply runs. The module system thus makes Zend Framework 2 be much more than just a web framework; indeed it goes far beyond this and is really a platform for integrated applications and functions.
In the next chapter we will elaborate on the technical details of the module system and take a look at how it functions internally and how modules are developed. This is important because even one’s own application is represented in code by a module. Modules are everywhere in the new Zend Framework! When you understand the module system, that’s half the battle both for the development of one’s own application and for embedding already implemented functions into it.
Further on in the book, we will take a look at the available modules because in addition to the already mentioned functions for user management, there is much more; for example, modules related to Doctrine 2, the well-known PHP-ORM system. These modules ship so-called "glue code", which allows one to easily use an external library in a Zend Framework 2 application.
Event system
Zend Framework 2 is decisively based on the concept of Event-driven Architecture. This approach builds on the idea that certain activities occur in a system after a specific event has previously taken place. To achieve this, activities register themselves for an alert when the event occurs. If the event occurs, the registered activities take place. Here is an analogy from the real world: When we wait at a bus stop (we have "registered" ourselves for the event of the bus’s arrival) and when the bus finally drives up (event occurs), its door opens (activity 1), we buy a ticket (activity 2), search for a vacant seat (activity 3), and the bus starts up (activity 4).
Here is another example: An article offered on eBay is sold. This event triggers a series of activities in the system:
A confirmation of purchase is sent to the purchaser.
A confirmation of sale is sent to the seller.
Sales fees are calculated and charged to the seller’s account.
The article in question is removed from the search index, so that it can no longer be found (it has already been sold).
If eBay decides at a later point in time to also inform the unsuccessful bidders that the auction has ended (in reality this has already been done), this activity can easily be added to other activities for this event without huge efforts.
However, EDA is a two-edged sword. On the one hand, one achieves an enormous flexibility in structuring workflows by employing this style of architecture. New activities can be easily added. In this manner, entire procedures can subsequently be easily modified. Flexibility is the decisive argument. In contrast, there is a certain lack of transparency regarding the things that all really take place in the application when an event occurs. Fundamentally, an activity for an event can be registered more or less anywhere in the application without this connection being visible at the location in code where the event subsequently really occurs. A further challenge is the sequence of the activities. If the seller in the eBay example given above, is also notified of the fees due (which are calculated on the basis of the final price in the auction), this activity must occur after the fees have been calculated (i.e. another activity had to take place). Now, things begin to get complicated. In a nutshell, EDA can result in processes that are difficult to understand. The causes of errors are more difficult to identify, and debugging applications is more complicated.
Despite this, the advantages outweigh the disadvantages by so much, particularly also in connection with the Framework’s module system, that the programmers decided to use EDA for Zend Framework 2. If one is familiar with the pitfalls, one can easily avoid them.
MVC implementation
Another building block of Zend Framework 2 is its MVC implementation. Although Zend Framework 2 also again provides the option of freely using its components and, for example, ignoring Zend\Mvc, in practical work one would only do this in exceptional cases. Indeed, in most cases, it is precisely the MVC implementation that is often the reason to use Zend Framework. Zend\Mvc structures an application via the logical separation of code components into "Models", "Views" and "Controllers", and in this manner ensures a certain order that is not only beneficial for the application’s serviceability and extensibility, but also promotes the reuse of functions.
And this is what Zend\Mvc in action looks like: After the Zend\ModuleManager, the central unit in the Module System, has prepared all available Modules for use, read in the configuration, and initialised additional components, the Zend\Mvc\Router ensures that a suitable Controller (a PHP class) is instantiated in a Module and the correct action (a method) is invoked. In this context, the routing is based on previously configured routes, which represent the mapping between URLs and Controllers or Actions, respectively. The selected controller refers back to the Request Object to further process it; it provides an object-oriented access to the Request Parameters, and is made available by the MvcEvent object. The latter, in turn, was generated at the start and will be made available at the appropriate locations. In the further course, the controller makes use of the application’s model and accesses persistent data, services and business logic. It ultimately generates the "view model". It holds the data to be displayed and together with the proper HTML template the result of the invocation is generated, maybe as well by utilizing so-called "view helper". Optionally, the output is now also inserted into a layout and the final result is returned to the client. But we will go into that in greater detail later.
Since Zend\Mvc extensively uses the Framework's Event System, a great many options for influencing the default flow sketched above are provided here. In this manner, for example, access control or an input filter can be implemented before the controller is executed. Before the return of the results, one could ensure that the generated HTML markup is error-free, if necessary, with the aid of HTMLPurifier.
The code for the MVC implementation is brand new for Zend Framework 2. The MVC implementation in Version 1 was still rather inflexible, whereas the new version is definitely more flexible and ultimately allows the configuration of any arbitrarily adapted workflows. Basically, the procedure sketched out above can also be completely differently structured using Zend\Mvc without having to dispense with the use of Zend Framework and the advantages resulting from its use.
Additional components
In addition to Zend\Mvc, Zend\View, Zend\ServiceManager, Zend\EventManager and Zend\ModuleManager, which jointly comprise the "framework core", Zend Framework 2 has a number of further components, which we will briefly consider in the following. The majority of these components will also be considered in a detailed manner again in the course of this book. At that time we will deal with each of the respective components more intensively. In contrast to many other frameworks, Zend Framework has always been so conceived that it is also possible to use only selected components, while other components are ignored.
When looking at the list of components, those who have used Zend Framework 1 will notice that some components no longer exist. In particular, the many components for linking up to diverse web services are no longer part of Framework in Version 2.0, but are maintained as independent projects or libraries. Thus, for example, Zend_Service_Twitter is no longer a part of the core, but is now administered in the GitHub account zendframework in its own repository. The idea behind this is to sharpen the Framework’s profile and to focus its application area. Nor does Zend_Registry exist in the new version. Its task is now performed by the ServiceManager, which (as we will see in the further course of the book) can also do much more.
The following components are part of Zend Framework 2:
Authentication: Serves to implement a "Login" function, in which the server checks whether or not a user really is the person whom he claims to be (for example, because he knows the secret password).
Barcode: Library for generating barcodes.
Cache & Memory: Generic implementation of a caching systems under consideration of different "Backends", for example "Memcached" or "APC".
Captcha: Generation of CAPTCHAs, for example for use in web forms.
Code: Tools for the automatic generation of code.
Config: Aid which handles reading and writing of application configurations in different formats, for example YAML or XML.
Console: Library for using application functionality, for example controllers, via a shell (instead of on the basis of a HTTP requests by a browser).
Crypt: Functions that handle encryption.
Db: Library for work with databases (but not an ORM system).
Di: Implementation of a Dependency Injection (DI) Container.
Dom: Library for server-side work with the DOM.
Escaper: Aid for output escaping.
Feed: Generation of RSS and atom feeds.
File: Aid for working with files.
Filter: Functions for filtering data, for example given by a web form.
Form: Library for the PHP based object-oriented generation of HTML web forms in conjunction with "Validators" and "Filters".
Http: Aid for dealing with HTTP.
I18n: Extensive library for the internationalisation of applications, e.g. the output of translated contents.
InputFilter: Allows the use of filters and validators on received data.
Json: Tools for the serialisation and deserialisation of JSON data structures.
Ldap: Library for dealing with LDAP systems, for example in conjunction with Authentication.
Loader: Autoloading functions for PHP classes as well as for loading MVC modules.
Log: Implementation of a generic logging functionality with support of different types of "Logging Backends".
Mail & Mime: Library for sending (multipart-)emails.
Math: Diverse mathematical aids.
Navigation: Generation and outputting of web site navigations.
Paginator: Generation and outputting of "Sheet Navigations", for example in results lists.
Permissions: Library for rights and role systems.
ProgressBar: Generation and presentation of progress bars (among others also on the command line)
Serializer: Tools for the serialisation and deserialisation of objects, for example for long-term storage.
Server, Soap & XmlRpc: Libraries for providing web services, e.g. on the basis of SOAP or XML-RPC. Part of Soap is also a helpful SOAP Client implementation.
Session: Administration of user sessions.
Stdlib: Diverse standard functions and objects, for example the implementation of a "Userland PriorityQueue", which, e.g., is used by the EventManager.
Tag: Functions for the administration of "Tags" and the generation, for example, of "Tag Clouds" on a website.
Text: Aid that handles the management of strings and scripts. Is used internally, e.g., by Captcha.
Uri: Functions for the generation and validation of URIs.
Validator: Extensive library for the syntactic validation of data, for example of entries in forms, for many application cases, among them ISBN, IBAN, email addresses and much more.
Version: Holds information on the used Framework Version and the available, newest version at GitHub in readiness.
Design Patterns: Interface, Factory, Manager, etc.
If one looks through Zend Framework 2 code for a time, one notices that it is crammed full of implementations of so-called Design Patterns. If one is more familiar with "typical PHP code"— and that is not intended to be judgemental in any way—it will take awhile before one finds one’s way around Zend Framework 2. If one has had much to do with Java, one will feel much more rapidly at home, simply because "design patterns" found their way into the Java world several years earlier or even evolved there, respectively. To simply your access to the material, let’s take a look at a few ordinary Zend Framework 2 constructs in the following. Not all of them are really Design Patterns in a strict sense, but we should ignore this fact for the moment. And we should also do the same with the fact that I use serviceable simplifications in my explanations at some places in the book. This is not a comprehensive book on design patterns and the knowledge to be imparted is primarily intended to help the reader develop an understanding for Zend Framework 2. At this time I should perhaps repeat the following advice: Of course, as an application developer it is not necessary to understand all the mechanics of Zend Framework 2 in detail. Quite the contrary: A framework is meant to reduce the work effort and to make it possible for one to concentrate entirely on the programming of the "business logic" itself. However, it is a great help if one has a fundamental understanding of the connections between the individual framework components—and even more important: of the basic concepts. Thus, it is worthwhile not to skip this chapter.
Interface
Interfaces are an inherent element of object-oriented programming, and PHP has supported them comprehensively since Version 5. Interfaces allow one to decouple invoking code from a concrete implementation. If one always develops one’s applications for a defined interface, one can rest assured that at runtime a concrete implementation with stipulated methods and properties will be available. Indeed, one can also use an alternative implementation without having to modify the invoking code.
Listener
A "listener" is a short string of code that is executed as soon as a defined event occurs in an application. Technically speaking, to achieve this, a listener is registered beforehand by a so-called "EventManager" (Attention: risk of confusion with a popular profession) for an event. In this manner, the connection convenes at this location.
ListenerAggregate
A "ListenerAggregate" herds a series of listeners together, for example, in order to register them with an "EventManager". Not much more, but also no less.
Factory
A "Factory" is always used when the instantiation of a specific object is complicated, i.e. when an entire series of manipulations are required to make an object ready for use. For example, Zend Framework uses a factory to instantiate its ModuleManager and additionally to register a number of module-relevant listeners.
Service
A "Service" provides access to specific files or functions. The term is extremely general and can have a completely different meaning in each case depending on the context. In the scope of the Framework a service is understood to mean an object that is made available by a ServiceManager and provides a defined service. Thus, for example, listeners registered in the ServiceManager are termed services—just as, e.g., the ModuleManager, but also Controller or "view helper", are.
Manager
A "manager" is an object that manages the administration of a specific type of other object in the system. For example, Doctrine 2 has a so-called "EntityManager", which administers "entities", i.e. certain persistent objects (e.g., in a shop offerings, categories, customers, orders, etc.) throughout their entire lifecycle and ensures that the entities are read from a database and changes are transparently returned.
Strategy
Behind the Strategy Design Pattern is the idea of swapping out algorithms, which one would otherwise "hard wire" at the respective location in the code, to a class of its own and thus to make it exchangeable. Thus, sorting algorithms, for example, are good candidates for this strategy pattern. The different algorithms, according to which, e.g., a product lists can be sorted (price increasing/decreasing, rating increasing/decreasing, etc.) are not permanently encoded, but instead realised in the form of a class of their own, which all implement a common interface, which specifies a sort() method. If one has once implemented this mechanism, any other arbitrary sorting procedure can be realised at a later time and then be added.
Model View Controller (MVC)
The MVC pattern decisively affects the structure of the application code because it logically separates those components from one another, which manage the display (View), the processing of user interaction (Controller) and "business logic" with its objects and services.
Actions
"Actions" are an approach for further structuring the code used for processing user interactions in controllers. They are therefore closely connected with the MVC pattern and also are used in Zend Framework 2.
View Helper
With the aid of "view helpers", code for presentation logic can be encapsulated and reused in a standardised way.
Controller Plugins
By means of "Controller Plugins" frequently used code can be organised for interaction processing and be used in several controllers.
Hello, Zend Framework 2!
Put away all the grey theory—let’s take a look at the Framework in action
As discussed in the last chapter, Zend\Mvc is an independent, optional, but essential component of the Framework. The following context always includes Zend\Mvc. Zend\Mvc also dictates its own application and in a certain manner also the directory and code structure . But that is actually quite practical. If one is already familiar with a Zend Framework 2 application, one can also orient oneself very quickly in other applications that are also based on the Framework. Although one does indeed have the freedom to establish a completely different directory and code structure, this would make life unnecessarily difficult, as we will see later. If an application to be created, it is wise to use Zend\Mvc from the very beginning. However, if one wants to extend an existing application with functions from Zend Framework 2, it is perhaps appropriate to initially dispense with Zend\Mvc completely or to first use it at a later time.
Zend Framework 1 and 2 in parallel:
One can also operate Zend Framework 2 in parallel to Version 1 and initially only use Zend Framework 2 intermittently.
Installation
In principle, Zend Framework 2 does not have to be tediously installed. One simply downloads the Code, makes it available over a web server with PHP installation and can begin immediately. However, the fact that the Zend Framework 2 Code alone is not enough to be able to actually see a Zend\Mvc-based application in action is a challenge because, as we have already mentioned, Zend\Mvc, i.e. the components which take over the processing of HTTP requests, is optional and accordingly is also not inherently "wired" for use. One must thus initially personally ensure that Zend\Mvc is so equipped with configuration and initialisation logic—the so-called "boilerplate code"—that it can also actually be used. Otherwise, one initially sees … nothing.
To avoid this effort and to make getting started with Version 2 as simple as possible, the so-called "ZendSkeletonApplication" was developed in the course of the Framework’s development; this serves as a template for one’s own project and includes the necessary "boilerplate code", which one would otherwise have to prepare oneself with great effort.
ZendSkeletonApplication
The installation of the "ZendSkeletonApplication" is the simplest with help from Git. To take advantage of this, it is first necessary to install Git on one’s own computer. On Mac systems and in many Linux distributions, Git is even already preinstalled. For installation on a Windows' system, Git for Windows is available for downloading. The installation under Linux nearly always runs under the respective package manager. After installation and after invocation of
$ git --version
on the command line, one should see this or a similar "sign of life":
> git version 1.7.0.4
From here onwards, everything is very easy—change to the directory in which the subdirectory for the application is to be set up and which can later be made available to the web server as "document root", and download the ZendSkeletonApplication .
$ git clone git://github.com/zendframework/ZendSkeletonApplication.git
Admittedly, in Git jargon it has to be termed "cloning" and not "downloading". But for the time being, we will ignore that. And by the way, do not be afraid of Git! One does not need any advanced Git knowledge in order to successfully work with this book and Zend Framework 2. Of course, the reader can also administer his or her own application code in the future even permanently— with Git, but it is not necessary. Therefore, a subversion, CVS or even no system at all can also be subsequently used for code administration without problems.
Downloading the "ZendSkeletonApplication"" is very fast, even for less rapid Internet connections, but one must always have such a connection in any case. The reason for the fast download is the fact that the Framework code itself is not downloaded at all; instead only the corresponding boilerplate code for the development of one’s own application, which is based on Zend Framework 2, is provided.
Composer
The "ZendSkeletonApplication" uses with Composer another PHT tool, which established itself for the management of dependencies for other code libraries some time ago. The idea behind the composer is as simple as it is ingenious. A configuration file contains a definition of the other code libraries that an application is dependent on and from where the respective library can be obtained. In this case, the application is dependent on Zend Framework 2, as can be seen by looking in the file composer.json in the application root.
{
"name": "zendframework/skeleton-application",
"description": "Skeleton Application for ZF2",
"license": "BSD-3-Clause",
"keywords": [
"framework",
"zf2"
],
"homepage": "http://framework.zend.com/",
"require": {
"php": ">=5.3.3",
"zendframework/zendframework": "2.*"
}
}
Listing 4.1
In lines 11 and 12, the application’s two dependencies are declared. Both PHP 5.3.3 or higher and the current version of Zend Framework 2 are required. The following two invocations ensure that Zend Framework 2 is downloaded and additionally also integrated in the application such that it is immediately utilisable and the corresponding framework Classes are made available by autoloading.
$ cd ZendSkeletonApplication
$ php composer.phar install
> Installing zendframework/zendframework (dev-master)
Composer has now downloaded Zend Framework 2 and made it available for the application in the vendor directory.
Phar-Archive:
A Phar Archive provides the option of making a PHP application available in the form of a single file. If one looks at the Composer-Repository at GitHub, it becomes clear that composer does not consist of a single file, as one might think, but that its components are merely bundled in a Phar Archive for distribution of the application.
Phar Archive and Suhosin:
If "Suhosin"" is used on a system, the use of Phar must initially be explicitly permitted such that the suhosin.ini is extended by the entry suhosin.executor.include.whitelist=phar. Otherwise, problems can occur in the execution of the Composer command.
Installation without Git or Composer:
If necessary, it is also possible to obtain the Framework and the "ZendSkeletonApplication" via a "normal download" or Pyrus (the successor to PEAR). Additional installation information is to be found on the official download site.
A first sign of life
We have now completed nearly all of the required preparations. Finally, we only have to ensure that the application’s public directory is configured as Document Root of the web server and can be called up/invoked via the URL `http://localhost by the browser.
For example, to achieve this, a directive in following exemplary form must be specified in the httpd.conf of Apache:
// [..]
DocumentRoot /var/www/ZendSkeletonApplication/public
// [..]
where it is required that the "ZendSkeletonApplication" was downloaded with the following command beforehand:
$ cd /var/www
$ git clone git://github.com/zendframework/ZendSkeletonApplication.git
Setting up a PHP runtime environment:
Since the scope of my readers’ previous knowledge is probably extremely different, I will not explain exactly how a web server is installed on a system together with PHP at this time, but instead presume that my readers already know this. For anyone who needs assistance, additional information and support are to be found in the Appendix of this book
If these configurations have been made, Zend Framework 2 should show itself for the first time when http://localhost is called up in the browser.
Directory structure of a Zend Framework 2 application
Now, we can finally look at it, a Zend\Mvc-based Zend Framework 2 application with its characteristic directory layout and the typical configuration and initialisation code:
ZendSkeletonApplication/
config/
application.config.php
autoload/
global.php
local.php
...
module/
vendor/
public/
.htaccess
index.php
data/
In our case the "Application Root" is the ZendSkeletonApplication directory, which is automatically generated by cloning the appropriate GitHub repository. In the config directory, there is, on the one hand, application.config.php, which contains the basic configuration for Zend\Mvc and its collaborators as a PHP array. In particular, the ModuleManager is configured there; we will frequently talk about its details in the course of the book. If required, the autoload directory contains additional configuration data in the form of additional PHP files; initially, this seems a bit strange, but one becomes accustomed to it. To begin with, the directory’s designation as "autoload" is a bit irritating. In this location, "Autoload" has nothing to do with the "Autoloading" of PHP Classes, but instead indicates that the configurations that are filed in this directory will be automatically taken into consideration. And that occurs chronologically after the configuration of the application.config.php and also after the configurations performed by the individual modules, which we will talk about later. This sequence of configuration evaluation is extremely important because it allows the situation-dependent overwriting of configuration values. The same principle applies to global.php and local.php: configurations in the global.php are always valid, but they can be overwritten by configurations in the local.php. Technically speaking, the Framework initially reads in the global.php and subsequently the local.php, whereby previously defined values can be replaced, if necessary. What is that good for? In this manner, configurations can be defined independently of the runtime environment. Let us assume that the programmers of an application have set up a runtime environment locally on their computers. Since a MySQL database is required for the application, all of the developers have installed this on their computers and in the process have configured the access rights such that passwords, which the respective developers also otherwise frequently use, are utilised. It is indeed more convenient. However, since each developer potentially has an individual password for the database, this configuration cannot be hard-wired, but must be individually specified. To achieve this, the developer enters his or her connection data in the local.php file, which he or she maintains locally in the computer and does not check into the code administration system either. Whereas the connection data for the "live system" are deposited in global.php file, every developer can work with his or her own connection data, which are defined with the aid of the local.php. In this manner even special configurations for test or staging systems can be deposited. Incidentally, configuration files of the form "xyz.local.php" (also applies to "global"), for example db.local.php, are also processed by the Framework as described above.
The individual modules of the application are located in the Module directory. Each module comes with its own typical directory tree, which we will take a closer look at later. However, at this time the important thing is that every module can also have its own configuration. We now have three places in which something can be configured: application.config.php, module-specific configuration and the global.php and local.php files (or their "specialisations" as described above), which the system reads in exactly this order and ultimately provide a large, common configuration object, because in the course of execution exactly these configurations are merged. If the configurations of application.config.php are only of interest in the first few meters of bootstrapping, the configurations of the modules and those from global.php and local.php are also important in the later course of the processing chain and are generously made available by the ServiceManager. We will also learn more about this later. The attentive reader realises at this time that as a result of this "configuration cascade", for example module configurations that flow into the application from third party manufacturers’ modules can be extended or even replaced. This is very practical.
The vendor directory contains conceptionally the code which one did not write oneself (ignoring the "ZendSkeletonApplication" code at this time, but which one could have had to write oneself in case of doubt) or which one did not write especially for this application. Zend Framework 2 is thus located approximately there, but, if necessary, also in other libraries. When dealing with additional libraries, one must always ensure that the corresponding classes can be addressed by the application. However, if one can install the respective library using composer, this work does not have to be done by the developer either. The installation of additional libraries should therefore in the ideal case always be performed using composer. The fact that also the ZF2 modules, which actually should be located in the module directory, can also be made available via the vendor directory is also interesting. (To be perfectly correct, one would have to say that is can be configured via application.config.php and the modules can therefore basically be deposited anywhere.) This means that third party manufacturers’ libraries that adhere to the Zend Framework 2 module standard can also be added in this manner. Thus, one can ensure that only those modules that one actually developed in the scope of the respective application are located in the module directory. All other modules can also be made available via vendor
All files that are to be made externally accessible via the web server (with the exception of specific restrictions in web server configuration) are located in public. This is also the place for images CSS or JS files as well as for the "central entry point", the index.php. The idea behind this is that every HTTP request that reaches the web server and a specific application initially results in calling up the index.php. Always. Regardless of how the URL call itself is formulated. The only exceptions are URLs that refer to an actually existing file within or below the public directory. Only in this case, does the index.php not perform the execution, instead the appropriate file is read and returned. This mechanism is achieved by a typical Zend Framework .htaccess file in the public directory:
RewriteEngine On
RewriteCond %{REQUESTFILENAME} -s [OR]
RewriteCond %{REQUESTFILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .*$ - [NC,L]
RewriteRule .*$ index.php [NC,L]
In order for this to function, several conditions must be fulfilled. On the one hand, the web server must be equipped with a so-called RewriteEngine, which must also be activated. On the other hand, the web server has to allow an application to set directives via its own .htaccess. To achieve this, the [following directive] must be exemplarily in the Apache httpd.conf`.
AllowOverride All
The data directory is relatively unspecific. Basically, data of all kinds, which have anything to do with the application (documentation, test data, etc.) or that are generated in the running time (caching data, generated files, etc.), can be deposited there.
The index.php file
Every request that does not map onto a file that actually exists in the public directory is thus redirected via the .htaccess file to the index.php. It therefore has a special importance for work with Zend Framework 2. At this time, it is again important to realize that the index.php itself is not part of the Framework, but that it is indispensible for using the Framework’s MVC components. Please remember: Zend\Mvc is the component that represents the "processing component" for an application. The index.php comes with the ZendSkeletonApplication; thus, we do not have to develop it ourselves.
Because of the importance of the index.php — both for the Framework and for our understanding of the Framework’s mechanics—we will now risk a detailed look at this very easily understood file:
<?php
chdir(dirname(__DIR__));
require 'init_autoloader.php';
Zend\Mvc\Application::init(include 'config/application.config.php')->run();
Listing 4.1
To begin with, we will change to the application root directory of the application, in order to be able to easily refer to other resources. Then init_autoloader.php is called up; this initially triggers autoloading by composer. This nondescript call-up ensures that all the libraries that have been installed by composer automatically make their classes available via autoloading mechanisms:
<?php
// [..]
if (file_exists('vendor/autoload.php')) {
$loader = include 'vendor/autoload.php';
}
// [..]
Listing 4.2
Consequently, we can dispense with all require() call-ups in the application. The few lines that I have written down here in such an emotionless manner actually represent an enormous attainment for us as PHP developers: It simply could not be easier to integrate libraries into one’s own application.
In the init_autoloader.php, the autoloading of the ZF2 classes via the environment variable ZF2_PATH or via Git submodule is then also alternatively ensured, just in case that one did not obtain Zend Framework via composer because in that case the above-mentioned autoloading mechanism of composer is sufficient. With the aid of the environment variable ZF2_PATH, for example, a number of applications in the system can use a central installation of the Framework code. Whether or not this is truly expedient, I cannot really say. Now, a brief check to see whether Zend Framework 2 can now be loaded—otherwise nothing will happen—and then we can get started:
<?php
// [..]
Zend\Mvc\Application::init(
include 'config/application.config.php')
->run();
Listing 4.3
The call-up of the class method init() of the Application initially ensures that the ServiceManager is superimposed. The ServiceManager is the central object in Zend Framework 2. It allows other objects to be accessed in many ways, is normally the "principal point of contact" in the processing chain and is also the first entry point in general. We will consider the ServiceManager later in greater detail. For simplicity’s sake, one can initially imagine the ServiceManager as a sort of global directory, in which an object can be deposited under a defined key. For all those who have already worked with Framework Version 1, the ServiceManager thus initially presents itself as a sort of Zend_Registry. At this point, we should perhaps make a small leap forward. Not only previously generated object instances come into consideration as values that can be deposited in the ServiceManager under a stipulated key, but also "Factories", which generate the respective objects—in the context of the ServiceManager analogously designated as "services". The underlying idea is that these services can only then be generated when they are really needed. This procedure is termed "lazy loading", a design pattern intended to delay memory and time-consuming instancing of objects for as long as possible. Indeed, some a number of services for some types of requests are never needed; why should the always be instanced beforehand?
But back to the code: The init() method is transferred to the application configuration as parameter, and this has already been taken into consideration by the generation of the ServiceManager:
<?php
// [..]
$serviceManager = new ServiceManager(
new ServiceManagerConfig(
$configuration['service_manager']
)
);
// [..]
Listing 4.4
At this point, the ServiceManager is now initialised and equipped with those services which are required in the scope of processing of requests by Zend\Mvc. However, the ServiceManager can also be effectively used for completely different purposes, beyond Zend\Mvc.
Subsequently, the application configuration itself is deposited in the ServiceManager for later use.
<?php
// [..]
$serviceManager->setService('ApplicationConfig', $configuration);
// [..]
Listing 4.5
Then the ServiceManager is asked to perform its services for the first time.
<?php
// [..]
$serviceManager->get('ModuleManager')->loadModules();
// [..]
Listing 4.6
The get() method requests a service. Incidentally, in this situation we already have a case in which the ServiceManager does not return an instantiated object, but instead uses a "factory" to generate the requested service, by acclamation as it were. In this case, the Zend\Mvc\Service\ModuleManagerFactory is used, and generates the requested ModuleManager.
But how does the ServiceManager actually know now that whenever the ModuleManager service is requested that the above-mentioned factory is to be called upon for its generation? Let us again look at the code ahead of it:
<?php
// [..]
$serviceManager = new ServiceManager(
new ServiceManagerConfig($configuration['service_manager'])
);
// [..]
Listing 4.7
As a result of the transfer of ServiceManagerConfig, the ServiceManager is prepared for the use of Zend\Mvc and has registered exactly that factory for the ModuleManager, among other things. In the following chapters, we will take another look at all of this in greater detail and also look at the other services which are provided as standard.
But let us now return to the code sequence: After the ModuleManager has now been made available via the ServiceManager, the loadModules() method initialises all the modules activated by the application.config.php, If the modules are ready, the ServiceManager is again contacted and the "application"" service is requested from it.
<?php
// [..]
return $serviceManager->get('Application')->bootstrap();
// [..]
Listing 4.8
This fact may appear a bit strange, especially since the entire processing sequence indeed originally began via a Zend\Mvc\Application. But it now becomes clear that its init() method initially only initialised the ServiceManager, whereas the Application itself is then itself generated as a service.
Now a very complex procedure, which is responsible for the processing of the request itself, begins. The "application" is prepared ("bootstrapping" occurs). Back in the index.php, the application is then executed and the result is returned to the calling program.
<?php
// [..]
Zend\Mvc\Application::init(include 'config/application.config.php')
->run();
// [..]
Listing 4.9
I have devoted a chapter in this book to the exact consideration of the request processing because of its importance, but also of its complexity. Until we get to it, we will keep this in mind: The index.php is the central entry point for all requests that are processed by the application. These very requests are technically rerouted to the index.php by .htaccess. The actual URL that was called up by the user is naturally maintained and is subsequently read by the Framework in order to locate an appropriate controller with its action. The ServiceManager is at the focus of the processing and gives access to the services of the application. Therefore, we must initially generate the ServiceManager, before it can, in turn, give us access to the ModuleManager, with whose help we can bring both the registered modules and the Application, which is responsible for processing the requests, to life. So far, so good.
Zend Framework 2 with alternative web servers:
Naturally, can alternative web server instead of Apache—such as nginx—be used. In this case, only the Apache-specific configuration as well as that of .htaccess are to be analogously transformed, for example with the help of the "nginx rules".
Preparing one’s own module
The actual application logic, i.e. the individual pages, templates, forms, etc., are encapsulated in modules. Now that we have the executable ZendSkeletonApplication at our disposal, it is time to prepare our own first module. Because we initially have to concentrate on the individual steps that are required to prepare and encapsulate our own module, we will start with the classic module. Hello, World!
Preparing the "Hello World" module
A Zend Framework 2 module is first and foremost characterised by a defined directory structure and a few files that have to be part of every module or those that can present if needed.
Module.php
config/
module.config.php
public/
images/
css/
js/
src/
Helloworld/
Controller/
IndexController.php
view/
helloworld/
index/
index.phtml
This structure must be created in a Helloworld directory in the module directory within the application. By convention, a module is its own namespace, which thanks to PHP 5.3 we can also designate as such natively. In Framework Version 1, the pseudo-namespaces still had to be used; this resulted in very long class designations, for example in Zend_Form_Decorator_Captcha_Word Fortunately, with PHP 5.3 and Zend Framework 2, this problem is a thing of the past.
To begin with, we will fill the Module.php file with life.
<?php
namespace Helloworld;
class Module
{
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
}
Listing 5.1
The Module class is assigned to the namespace that is stipulated by our module, in this case Helloworld. The class itself is a normal PHP class, which can have a series of methods, which can be called up by different Framework managers and components, for example in the scope of the initialisation. The getConfig() method is also among them. As already in Zend Framework 1, the "convention over configuration" approach is also extensively used in the new version. This means that there are conventions (agreements) that, if used as agreed upon, make further configuration unnecessary. In this case the following convention has been stipulated: If you implement a getConfig() method in your module class, it will be called up in the scope of the initialisation of the ModuleManager. No sooner said than done! However, our method itself does not immediately return the configuration, but to achieve this it instead reads the module.config.php file in the module’s config directory, which then has the following contents.
<?php
return array(
'view_manager' => array(
'template_path_stack' => array(
__DIR__ . '/../view'
)
)
);
Listing 5.2
To begin with, it becomes apparent that the configuration for a module is mapped via a PHP array. There are fundamentally a large number of options as to how one can maintain configurations, for example as INI file, via YAML or as XML structure. All these structures require more or less complex parsing. However the most efficient and in the Framework the preferred method is to immediately deposit the configuration in PHP code. This makes any parsing unnecessary and a slender include() already ensures the desired effect of reading in the configuration. But here again, "convention over configuration" also applies. If we define a view_manager section in our configurations, these values will always be subsequently considered when searching for the correct template as if by magic. Thus, we configure here the directory in which our module’s Views (the HTML templates) will be deposited. Accordingly, at this time there is no "convention over configuration", but rather explicit information.
Moreover, we should specify in the Module.php how the autoloading of the individual module classes it to function. To achieve this, we implement the getAutoloaderConfig() method that will be processed during the initialisation of theModuleManager`—once again, according to convention.
<?php
// [..]
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__
)
)
);
}
// [..]
Listing 5.2
We will return to the "Autoloading" topic again later in more detail and now will have to be satisfied with the knowledge that the construct described above ensures that the classes of this module—especially also the controller—will be automatically loaded and can thus also be taken into consideration by the Framework. The Module.php file now looks like this:
<?php
namespace Helloworld;
class Module
{
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__
)
)
);
}
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
}
Listing 5.3
Now we will dedicate ourselves to the IndexController in the /src/Helloworld/Controller directory:
<?php
namespace Helloworld\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class IndexController extends AbstractActionController
{
public function indexAction()
{
return new ViewModel(array('greeting' => 'hello, world!'));
}
}
Listing 5.4
To begin with, we will again pay attention to the namespace. Our IndexController thus belongs to the Helloworld module and is a Controller there. So far, so good. The class inherits from the Framework class Zend\Mvc\Controller\AbstractActionController everything that makes it what is now is: a class that can process a request ("dispatching") and in the process uses its actions. The term "actions" means public methods that again conform to a certain name convention: a method of the class then becomes an "action" when "action" is appended to the method designation. The controller now has an action, the indexAction.
Many things customarily now occur inside the indexAction, such as the processing of request parameters, writing or reading of date from databases or accessing remote web services. Normally, the action does not do everything itself (in this context one otherwise also speaks of the so-called "fat controller"), but rather delegates the individual tasks to other fellow campaigners. This approach normally increases the reusability and serviceability of the code.
As a rule, an action ends its work by making the results of the operations performed available for presentation in the calling program’s browser. In Zend Framework 2 all the required data are returned (that’s something that is normally only said in tennis) in the form of a so-called "view models". To put it simply, a "view model" represents the data underlying a "user interface (UI)" and additionally also controls the status of certain UI components. We will so into this in more detail again later.
When we now desire to display the salutation "hello, world!" as heading in a browser, the appropriate h1 tag is still missing. Since the so-called "view" is responsible for the HTML presentation in a MVC-based application, we now have to create this (strictly speaking we are really only generate a "view template" that produces the desired result in the scope of "rendering" a "view" and on the basis of a "view model". For every action there is normally exactly one view or a view template, respectively. To achieve this, we enter the following in the index.phtml file in the view/helloworld/index directory:
<h1><?php echo $this->greeting; ?></h1>
Listing 5.5
Also in the structuring of the view, the module’s namespace, but also the designation of the controller and that of the action, plays a role, as can be clearly seen. Our view is located in a view directory. That is fixed and does not change. Then in a subdirectory that is named after the module, and there again in a subdirectory that is named after the controller. The view itself bears the name of the respective action with the suffix ".phtml". A file with the ".phtml" ending comprises by convention PHP code and HTML markup, where by the PHP code is restricted to the presentation and should not, for example, contain business logic. Incidentally, the "phtml" suffix stands for PHP + HTML. In our view file, we thus have HTML markup and then access the view’s data model via $this, which we generated in the controller beforehand. We access the key greeting directly with "greeting", for which we filed the value "hello, world!" in the view model. We then output it by means of echo.
But we are still not finished. We want to see "hello, world!" on the screen when we call-up the URL http://localhost/sayhello. To achieve this, we have to extend the configuration of the module in the module.config.php by a corresponding route and the details of our controller.
<?php
return array(
'view_manager' => array(
'template_path_stack' => array(
__DIR__ . '/../view',
),
),
'router' => array(
'routes' => array(
'sayhello' => array(
'type' => 'Zend\Mvc\Router\Http\Literal',
'options' => array(
'route' => '/sayhello',
'defaults' => array(
'controller' => 'Helloworld\Controller\Index',
'action' => 'index',
)
)
)
)
),
'controllers' => array(
'invokables' => array(
'Helloworld\Controller\Index'
=> 'Helloworld\Controller\IndexController'
)
)
);
Listing 5.6
Analogous to the view_manager key, the router key ensures—by convention—that the configuration of the routing of the corresponding Framework components is made accessible. Since we will consider routing later in more detail, only this much will be said at this point: At this time, we transfer an array with individual routes, one of which we have named "sayhello". It should always take effect when the "/sayhello" string follows the host information (in our case localhost) in the URL. If this is the case, the Framework should ensure that the IndexController, which we have just prepared, and in it the action index is executed. And that is actually everything. Due to the nested array notation, the configuration initially appears to be a bit unclear. But after a short time, one has quickly become accustomed to it.
It is interesting to note that we specified Helloworld\Controller\Index as the value for the controller although the control is indeed named IndexController. The explanation is to be found somewhat further on.
<?php
// [..]
'controllers' => array(
'invokables' => array(
'Helloworld\Controller\Index'
=> 'Helloworld\Controller\IndexController'
)
)
// [..]
Listing 5.7
With this small piece of code, we define a distinct name for our controller that is valid across all of the application’s modules. To ensure that it is unambiguous, we placed the designation of the controller in front of the module’s name. Helloworld\Controller\Index is thus now the symbolic name for our controller, which is correspondingly used in the route configuration.
Last but not least, we now have to extend the application.config.php file such that our new module will be considered at all. To achieve this, we amend the name of our module in the modules section.
<?php
// [..]
'modules' => array(
'Application',
'Helloworld'
)
// [..]
Listing 5.8
The URL http://localhost/sayhello should now provide the desired result and output "hello, world!" to the screen.
Autoloading
As a general rule, classes must initially be made available by means of a require() call (or something similar) before they can be used the first time, for it is indeed so that during the execution of a script only the code that was previously made available to the PHP interpreter is accessible there. Thus, it is not enough to program a PHP class, to deposit the former somewhere in the file system and the latter at another location, to refer to the script that is being executed without having made the class known beforehand. And now a brief digression: One must absolutely differentiate between code that is part of the PHP core and the so-called "userland" code. Whereas, for example, the core class allows \DateTime to be used without previous registration, this does not apply for classes that you have written yourself, i.e. userland code. Such code must always be made known to the PHP interpreter initially, and the respective PHP files in which the class definitions are located must have been loaded.
Zend Framework 2 makes intensive use of autoloading. Autoloading simply means that the registration of classes is performed automatically; the respective PHP files with the classes that are defined there are thus automatically loaded when needed. In order for this to function, a certain configuration must be performed—as we have already seen in the getAutoloaderConfig() method in the Module.php file.
Indeed, we avoid a great deal of typing on every page with autoloading because we would otherwise have to load each file with an explicit require(); but, on the other hand, we burden the system additionally with the autoloading function. Thus, autoloading has certain costs that can make themselves felt in the execution time of a Zend Framework 2 application. The good thing is that are different ways, among them high-performance ones, in which autoloading can be implemented, which are all supported by the Framework. Zend Framework 2 has two essential classes that are used for autoloading.
Standard autoloader
The standard autoloader is the implementation that has meanwhile become the customary manner of realising autoloading. In this context, the class name is translated one-to-one into a file name. Consequently, the corresponding loader expects, for example, that the Zend_Translate class (from Zend Framework 1) is defined in a Translate.php file in the Zend directory. This also applies to classes that make use of "real namespaces": A class Translate that is defined in the Zend namespace is expected to be at the same location in the file system. This convention corresponds to both the PEAR-Standard and the PSR-0 of the PHP Framework Interoperability Group](https://github.com/php-fig/fig-standards). The important thing is than one thinks of stating where the corresponding directory for the respective (pseudo-)namespace is located in the file system, as we did in the Module.php file.
<?php
namespace Helloworld;
class Module
{
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__
=> __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
// [..]
}
Listing 5.9
The include_path is namely no longer consulted, which should accelerate the loading of classes to the greatest possible extent. One does not really have to know any more at this time. If one conforms to these conventions, the classes will be automatically loaded without any problems.
ClassMapAutoloader
However, the highest-performance implementation of autoloading is the ClassMapAutoloader; it operates on the basis of a simple, associative PHP array, which contains the fully-qualified class names as key in each case and the appropriate file names as value. It looks approximately like this:
<?php
return array(
'PhlyContact\Service\ContactControllerFactory'
=> __DIR__ . '/src/PhlyContact/' .
'Service/ContactControllerFactory.php',
);
Listing 5.10
If the corresponding class is requested, the loader looks in the array for the appropriate value and loads the file. That’s it. In this case the disadvantage is obvious: The class map has to be continuously maintained. If a certain class is not located there, the autoloading fails. Fortunately, there is, on the one hand, the possibility of letting a ClassMap be generated automatically (e.g. in the scope of a build process) to ensure that one did not forget any class, and, on the other hand, several methods of autoloading can be combined.
<?php
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php'
),
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
Listing 5.11
In this manner, the ClassMap will be initially consulted and if necessary the PSR-0 mechanism will be resorted to if no hits have occurred up to that point.
The ClassMap itself is located in a file of its own outside the Module.php and is only referenced from there. The underlying idea is to generalize the procedure for the implementation of autoloading to the extent that—even outside of Zend Framework—libraries from different sources can be integrated without problems. In the last several years, some code libraries have already realized that it is a real added value for the user if the library provides some form of support for automatically loading the library’s classes. The problem with this is that in each case it is again a matter of isolated solutions. In the concrete realisation of autoloading, each library goes its own way in case of doubt; and even if the ways are similar, they indeed differ in detail. In the scope of the modules of a ZF2 application, the Framework orients itself on an additional standard from which, for example, the previously mentioned composer profits and in this manner to allow "translibrary" autoloading in the simplest way possible. For this purpose, every module is provided with three additional files relevant to autoloading: autoload_classmap.php, autoload_function.php und autoload_register.php.
The autoload_classmap.php returns a PHP array as mapping of class names and file names as described above, which any arbitrary autoloader—not only that of Zend Framework, but also, for example, that of composer—can process and if necessary also even combine them with ClassMaps of other libraries. In contrast, autoload_function.php returns a PHP function:
<?php
return function ($class) {
static $classmap = null;
if ($classmap === null) {
$classmap = include __DIR__ . '/autoload_classmap.php';
}
if (!isset($classmap[$class])) {
return false;
}
return include_once $classmap[$class];
};
Listing 5.12
The returned function can also be processed by an autoloader, for example, in the form that it can be considered as an additional source for the autoloading of classes. Ultimately, this function also again accesses the ClassMap. And autoload_register.php is even more slender:
<?php
spl_autoload_register(include __DIR__ . '/autoload_function.php');
Listing 5.13
In this case, the previously defined autoloading function, which again accesses the ClassMap proper, is directly registered for autoloading and not returned to the calling program again. A simple
<?php
require_once 'autoload_register.php';
Listing 5.14
then insures that the autoloading for exactly these components functions. This is, however, without the option of performing further optimisations, for example in the processing sequence of all registered autoloading functions.
The three autoloading files are, as we have already seen in the previous chapter, not required for a module to function because all of these files refer to the autoloading ClassMap, which itself is not absolutely necessary, but can noticeably improve the performance of an application.
In conclusion, a complete module directory layout for the "Hello world" module complete with the autoloading files is then depicted as follows:
Module.php
autoloadclassmap.php
autoloadfunction.php
autoload_register.php
config/
module.config.php
public/
images/
css/
js/
src/
Helloworld/
Controller/
IndexController.php
views/
Helloworld/
Index/
index.phtml
One time Request and back again
Let us look at exactly what happens to a request and how the Framework’s different collaborators generate the answer with our "hello, world!" on the browser.
Everything begins with the call-up of the http://localhost/sayhello URL. The HTTP request reaches the web server, which after consulting .htaccess decides that the index.php has to be processed by the PHP interpreter. In the scope of the index.php, the autoloading is initially configured and subsequently the application’s init() method is called up.
<?php
Zend\Mvc\Application::init(
include 'config/application.config.php')
->run();
Listing 6.1
ServiceManager
The ServiceManager instantiated there:
<?php
// [..]
$serviceManager = new ServiceManager(
new ServiceManagerConfig($configuration['service_manager'])
);
// [..]
Listing 6.2
As a result of the transfer of ServiceManagerConfig, the ServiceManager is equipped with several standard services which Zend\Mvc requires for smooth functioning. In addition the corresponding configuration is transferred to the ServiceManagerConfig from the previously loaded application.config.php.
The ServiceManageris a sort of Zend_Registry with extended functions. Whereas the Zend_Registry of Version 1 could merely file existing objects under a certain key and subsequently load them again (key value storage), the ServiceManager goes several steps further.
Services and service generation
In addition to the administration of services in the form of objects, a "generator" can also be registered for a key, which generates the respective service initially when needed. To achieve this, either classes (fully qualified, i.e., if necessary, with declaration of the namespace, which are then on request instantiated, for example in the form
<?php
$serviceManager->setInvokableClass(
'MyService',
'Helloworld\Service\MyService'
);
Listing 6.3
or factories can be deposited.
<?php
$serviceManager->setFactory(
'MyServiceFactory',
'Helloworld\Service\MyServiceFactory'
);
Listing 6.4
In order for the ServiceManager to be able to manage the MyServiceFactory, the latter has to implement the FactoryInterface, which requires a createService method. This method is then called up by the ServiceManager. As light-weight implementation of a factor, a Callback function can be directly transferred.
<?php
$serviceManager->setFactory(
'MyServiceFactory',
function($serviceManager) {
// [..]
}
);
Listing 6.5
Alternatively, can an abstract Factory also be analogously filed, where by this is added without Identifier; this is shown here exemplarily in a Callback variant.
<?php
$serviceManager->addAbstractFactory(
function($serviceManager) {
// [..]
}
);
Listing 6.6
In addition, so-called "Initializers" can be filed; they ensure that a service is equipped with values or references to other objects when it is called up. "Initializers" can be so conceived that the inject objects into a service when the respective service implements a defined interface. We will see this in action again later. At the moment, we should only remember that they exist. All services made available by the ServiceManager are so-called "shared services", this means that the instance of a service—generated when needed or already present—can also be returned at a second request of just this service instance. The instance of the service is thus reused. This is valid for all standard services defined by the Framework; the only exception in this context is the EventManager. If a new service is registered, one can also prevent the reuse of instances.
<?php
$serviceManager->setInvokableClass(
'myService',
'Helloworld\Service\MyService',
false
);
Listing 6.7
Standard services, Part 1
At the very beginning of request processing, the ServiceManagerConfig ensures that a number of standard services are made available. It is important to realize that there are services in the ServiceManager, which can in part only be used by the Framework (or more exactly by its MVC implementation), whereas some other services are also useful for the application developer, for example in the context of a Controller. This will become clearer somewhat further along.
Invocables
The following service is made available as an "invocable", i.e. by specifying a class, which is then instantiated when necessary.
SharedEventManager (Zend\EventManager\SharedEventManager): Allows the registration of listeners for certain events, also when the event manager required for this is not yet available. The SharedEventManageris automatically made available by a new EventManager when this is generated by the ServiceManager. Further explanations of the SharedEventManager will be found later in the book.
Factories
In the context of the ServiceManager, Factories are there to make services available, which do not exist until the real request occurs, but rather are built by a Factory "on demand". The following services are made available indirectly by a factory as standard.
SharedEventManager (Zend\EventManager\SharedEventManager): The EventManager can generate events and inform registered listeners about them. It can also be requested via the Zend\EventManager\EventManagerInterfacealias.
ModuleManager (Zend\Mvc\Service\ModuleManagerFactory): Administers the modules of a ZF2 application.
Configuration
The ServiceManageris thus decisively controlled for use in the scope of request processing by two configurations: The ServiceManagerConfig, which defines a number of standard services for request processing, and also by the application.config.php or module-specific configurations, respectively. In each case, the service_manager key is essential for this:
<?php
return array(
// [..]
'service_manager' => array(
// [..]
),
),
// [..]
);
Listing 6.8
Below the service_manager` keys, the following keys are then possible:
services: Definition of Services with the aid of already instantiated objects.
invocables: Definition of services by declaration of a class, which is instantiated when needed.
factories: Definition of factories, which instantiate serves.
abstract_factories: Definition of abstract factories.
aliases: Definition of aliases.
shared: Allows the explicit declaration of whether a certain service can be used a number of times or should be re-instantiated if again required.
As soon as the ServiceManager is available, the application.config.php is, then as a whole, i.e. also with the other non-ServiceManagerrelevant sections, itself made available as service.
<?php
// [..]
$serviceManager->setService('ApplicationConfig', $configuration);
Listing 6.9
This is important because other components, such as the ModuleManager or ViewManager, also access these services.
At the present time, the ServiceManager (equipped with diverse standard services) is thus in readiness and, in a manner of speaking, is only waiting for the show to begin. For, up to now not much has happened except for a few basic preparations. In fact, at this point nearly all of the above-mentioned services do not yet exist because they have not yet been requested and are only generated when necessary.
Writing a service of one’s own
Let us draw up a service of our own for our "Hello world" module in an exemplary manner. To achieve this, we initially add another Service subdirectory in the src/Helloworld directory of our module.
Module.php
config/
module.config.php
public/
images/
css/
js/
src/
Helloworld/
Controller/
IndexController.php
Service/
GreetingService.php
view/
Helloworld/
Index/
index.phtml
There we create a GreetingService class. This class must not implement any special interfaces or be derived from any basic classes; it is thus a so-called "POPO", a "Plain Old PHP Object". The only important thing is that we do not forget to make the class available in the right namespace.
<?php
namespace Helloworld\Service;
class GreetingService
{
public function getGreeting()
{
if(date("H") <= 11)
return "Good morning, world!";
else if (date("H") > 11 && date("H") < 17)
return "Hello, world!";
else
return "Good evening, world!";
}
}
Listing 6.10
Make the service available as an invocable
To use this class as a service in our controller and to be able to display a time-oriented greeting, we must add the class to the ServiceManager as Service. We can do this is the scope of our module in two ways: In the course of module configuration (module.config.php) by adding the section
<?php
// [..]
'service_manager' => array(
'invokables' => array(
'greetingService' => 'Helloworld\Service\GreetingService'
)
)
// [..]
Listing 6.11
or programmatically by adding the getServiceConfig() function in the Module.php:
<?php
public function getServiceConfig()
{
return array(
'invokables' => array(
'greetingService'
=> 'Helloworld\Service\GreetingService'
)
);
}
Listing 6.12
Both ways lead to the objective. Our service is now available in the form of an "invocable". We can request the service in the IndexController of our "Hello World" module and use it:
<?php
// [..]
public function indexAction()
{
$greetingSrv = $this->getServiceLocator()
->get('greetingService');
return new ViewModel(
array('greeting' => $greetingSrv->getGreeting())
);
}
Listing 6.13
Making the controller available via a factory class
However, we do have one problem now: The controller is dependent on a service (and the ServiceManager), which it actively accesses. Admittedly, it does not instantiate the class itself, which is good; thus, it does provide us with a possibility of making an alternative implementation available in the ServiceManager, if necessary, but it actively ensures that all dependencies have been resolved. At the latest, that will create problems for us when we desire to perform unit testing. A possible alternative in this case would be the previously mentioned "dependency injection" or "inversion of control". In this context, the required collaborators are automatically made available and must no longer be actively requested. In the framework of the ServiceManager, we can realise this procedure, for example, via a preceding factory.
To achieve thus we prepare the IndexControllerFactory factory in the same directory in which the IndexController has been deposited:
<?php
namespace Helloworld\Controller;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class IndexControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$ctr = new IndexController();
$ctr->setGreetingService(
$serviceLocator->getServiceLocator()
->get('greetingService')
);
return $ctr;
}
}
Listing 6.14
In addition, we alter the module.config.php in the controllers section as follows:
<?php
// [..]
'controllers' => array(
'factories' => array(
'Helloworld\Controller\Index'
=> 'Helloworld\Controller\IndexControllerFactory'
)
)
// [..]
Listing 6.15
From now on, our IndexController is thus no longer generated by instantiation of a defined class, but by the deposited factory, which was previously organised by the controller’s collaborators and in this case placed at the disposal of the controller by "Setter Injection". We still have to add the corresponding "Setter" to the IndexController and in the course of the action itself to access the corresponding member variable, instead of accessing the ServiceLocator.
<?php
namespace Helloworld\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class IndexController extends AbstractActionController
{
private $greetingService;
public function indexAction()
{
return new ViewModel(
array(
'greeting' => $this->greetingService->getGreeting()
)
);
}
public function setGreetingService($service)
{
$this->greetingService = $service;
}
}
Listing 6.16
Thus, a factory class can be used for both the generation of a service and for the generation of a controller. Indeed, it is as follows: The Zend\ServiceManager is employed in ZF2 in several ways. Once in the form which we have already discussed: as central instance via which even the Application itself is generated, i.e. the "container" for the entire application, if you wish to call it that. And then there is, as we will soon discuss in more detail, a number of specialised "ServiceManagers", for example one only for the application’s controller. In this context, the following lines of the IndexControllerFactory are of particular interest:
<?php
public function createService(ServiceLocatorInterface $serviceLocator)
{
// [..]
$ctr->setGreetingService(
$serviceLocator->getServiceLocator()
->get('greetingService')
);
// [..]
}
// [..]
Listing 6.17
It is apparent that initially getServiceLocator() is invoked in the $serviceLocator. The reason for this is the fact that the factory’s createService() method is always transferred to the "ServiceManager", which has been charged with the generation of the service, thus, in this case, the ControllerLoader(is automatically called up by the Framework) which was reserved for the generation of controllers. However, this in turn does not have any access to the GreetingService, which we prepared beforehand and which we only made available in the "central ServiceManager" (it is indeed ultimately not a controller). In order that the services of the "central ServiceManager" can now be made available despite this, the ControllerLoader accesses the central ServiceManager via the getServiceLocator(), and makes the former available to it by just these methods. Somewhat later in this chapter you will learn more about the details of this mechanism.
Making the controller available via a factory callback
However, with the IndexControllerFactory we now have an additional class in our source code. For the time being, this is basically not a problem, but it could be a bit too much of a good thing in a case like this, in which it is not much of a challenging task to generate the factory. A light-weight alternative, which also makes it possible for us to avoid direct dependence, is the use of a callback function as a factory in the module.config.php:
<?php
// [..]
'controllers' => array(
'factories' => array(
'Helloworld\Controller\Index' => function($serviceLocator) {
$ctr = new Helloworld\Controller\IndexController();
$ctr->setGreetingService(
$serviceLocator->getServiceLocator()
->get('greetingService')
);
return $ctr;
}
)
)
// [..]
Listing 6.18
The code for the generation of the IndexController, which was previously located in the IndexControllerFactory, has now been moved directly into the module.config.php.
Make the service available via Zend\Di
Regardless of which form of Factory is used, in all of them the generation of the respective object occurs programmatically, this means that appropriate PHP code must be written. Zend\Di provides an alternative option with whose help we can generate entire object graphs via configuration files. We will go into more detail later.
ModuleManager
But now let us return to request processing: After the ServiceManager has been adequately prepared, it has also been used for the first time, to generate the ModuleManager via the registered factory before loading the module is initiated.
<?php
$serviceManager->get('ModuleManager')->loadModules();
Listing 6.19
Generation of the ModuleManager
The ModuleManagerFactory serves the purpose of providing the ModuleManager service. As we remember, a factory is always used when the generation of an object becomes more complex. In the scope of this generation, initially a new EventManager is requested from the ServiceManager and is placed at the disposal of the ModuleManager. The ModuleManager is therefore able to generate events and to inform registered "Listeners" beforehand. The following events are triggered by the ModuleManager (at a later point in time!):
loadModules: Is initiated when the modules are loaded.
loadModule.resolve: Is initiated for each module that is to be loaded when the necessary data are read in.
loadModule: Is initiated during loading of a module for every module when the data that have been read in are exported.
loadModules.post: Is initiated after all modules have been loaded.
Module-oriented listeners
However, the ModuleManagerFactory does much more. To begin with, it generates a large number of listeners that are registered for the above-mentioned events.
ModuleAutoloader: Ensures that the Module class of the individual modules can be automatically loaded.
ModuleResolverListener: Instantiates the Module.php of the respective module.
AutoloaderListener: Invokes the getAutoloaderConfig() method in Modules, in order to obtain information on how the modules’ classes can be automatically loaded.
OnBootstrapListener: Checks to determine whether Modules have an onBootstrap() method and registers the invocation of this method for the bootstrap event that will be triggered at a later point in time by the Application.
InitTrigger: Checks to determine whether Modules have the init() method at their disposal. If they do, it is invoked.
ConfigListener: Checks to determine whether Modules have agetConfig() method at their disposal, which, if present, is invoked and the returned module configurations array is united with the other configurations.
LocatorRegistrationListener: Insures that instances of all Module classes that implement the ServiceLocatorRegisteredInterface are injected into theServiceManager`.
ServiceListener: Calls the getServiceConfig(), getControllerConfig(), getControllerPluginConfig(), getViewHelperConfig() methods in the Module class, if present (or reads out the corresponding configurations; further details on this in the following), processes the merged configurations of all modules, applies them to the m ServiceManager and adds further Standard-Services to the latter.
Standard services, Part 2
After a number of standard services have been made available in the course of the generation of the ServiceManager—among them, in addition to theEventManager, even the ModuleManager itself—the ServiceListener additionally registers (at the request of its factory) a colourful assortment of additional Services, which are required in the course of the request processing. At this point a brief comment is appropriate: at this point in time the operational mode of each service can and should not be completely understood! Many of the services that are registered by the ModuleManager at this time will cross our path again in the course of this chapter or book. The following list thus should serve much more as a reference and outlook to that which is still to come. The following services are thus—listed here according to manner of registration—registered.
Invocables
RouteListener (Zend\Mvc\RouteListener): Listens later to the Mvc result onRoute and then ensures that the router is charged with the resolution on the appropriate controller.
DispatchListener (Zend\Mvc\DispatchListener): Listens later to the Mvc result onRoute and then ensures that the ControllerLoader loads the previously selected controller and runs it.
Factories
Application (Zend\Mvc\Service\ApplicationFactory): The Application (generated by the deposited factory) represents, so to speak, the entire processing chain and in general the entire application.
Configuration (Zend\Mvc\Service\ConfigFactory): The generated Config service returns the merged configuration for the application. It is also available via the Config alias.
ConsoleAdapter (Zend\Mvc\Service\ConsoleAdapterFactory): Service for accessing the command line.
DependencyInjector (Zend\Mvc\Service\DiFactory): Zend Framework has its own implementation of the so-called "dependency injection", with whose help complex object graphs based on a comprehensive configuration are automatically "merged". Instead of the DependencyInjector keys, one can also use its aliases Di or Zend\Di\LocatorInterface. We will look at Zend\Di in more detail later.
Router, HttpRouter, ConsoleRouter (Zend\Mvc\Service\RouterFactory): Based on the request URL, the factory-generated Router service determines the controller that is to be invoked—if necessary, also in the "command line mode".
Request (Zend\Mvc\PhpEnvironment\Request): Provides access to all request information, e.g. the request parameters.
Response (Zend\Http\PhpEnvironment\Response): Represents the answer generated in the course of processing to the client.
ViewManager: The ViewManager performs a function for the administration of views and their processing that is similar to that performed by the ModuleManager for the modules and the ServiceManager for the services. It ensures that the data will sometime become, for example, web pages with HTML markup.
ViewJsonRenderer (Zend\Mvc\Service\ViewJsonRendererFactory): Allows the realisation of RESTful controllers and thus of web services, which conforms to the REST architecture style. This topic is discussed in a chapter of its own in the book.
ViewJsonStrategy (Zend\Mvc\Service\ViewJsonStrategyFactory): Ensures that the ViewJsonRenderer will be invoked when required. In the scope of this Strategy, for example, the system checks to see whether the ViewModel returned by the controller is of the JsonModel type.
ViewFeedRenderer (Zend\Mvc\Service\ViewFeedRendererFactory): Allows the realisation of RSS or Atom feeds of the "view data" returned by a controller.
ViewFeedStrategy (Zend\Mvc\Service\ViewJsonStrategyFactory): Ensures that the ViewFeedRenderer will be invoked when required. Part of this Strategy the determination of whether the ViewModel returned by the controller is of the FeedModel type.
ViewResolver (Zend\Mvc\Service\ViewResolverFactory): Makes it possible to find "view templates".
ViewTemplateMapResolver (Zend\Mvc\Service\ViewResolverFactory): Makes it possible for the ViewResolver to find View-Templates on the basis of a map.
ViewTemplatePathStack (Zend\Mvc\Service\ViewTemplatePathStackFactory): Makes it possible for the ViewResolver to find View-Templates on the basis of a list of paths.
And additionally:
ControllerLoader (Zend\Mvc\Service\ControllerLoaderFactory): TheControllerLoader can load a controller that was previously localised by a routing.
ControllerPluginManager (Zend\Mvc\Service\ControllerPluginManagerFactory): Makes the ControllerPluginManager and, thus, a number of plugins, which can be used in controllers, are available; among them, for example, the redirect plugin by means of which forwarding can be realised. This service can also be requested via the ControllerPluginBroker keys, Zend\Mvc\Controller\PluginBroker or Zend\Mvc\Controller\PluginManager.
ViewHelperManager (Zend\Mvc\Service\ViewHelperManagerFactory): Generates the ViewHelperManager, which is responsible for the administration of so-called "view helpers".
The latter three services are particularly interesting, because they, in turn, comprise the new "ServiceManager", termed "Scoped ServiceManager" in ZF jargon. Whew, now things are beginning to get a bit complicated! So let’s take a slow look at things—step by step. To begin with we should remember that there is the one "central ServiceManager" in the system. All of the important "application services" are generated by using it. It is both a ServiceManager in a technical sense and the conceptional "central ServiceManager" for us. However, there are specific services that the ServiceManager itself does not provide, but instead are made available by specialised "sub-ServiceManagers" or "scoped ServiceManagers", respectively, which can also provide services via the known mechanisms, i.e. "invocables", "factories", etc. All of them are also ServiceManagers in a technical sense.
In this context, let’s again take a look at the last chapter, in which we wrote our own controller. There we find the following passage in the module.config.php:
<?php
// [..]
'controllers' => array(
'invokables' => array(
'Helloworld\Controller\Index'
=> 'Helloworld\Controller\IndexController'
)
)
// [..]
Listing 6.20
When this configuration fragment is interpreted, this results in reference to the appropriate controller class under the Helloworld\Controller\Index key, which is registered as an "invocable" in the ControllerLoader, one of the standard "scoped ServiceManagers". Thus, if this controller is subsequently identified as appropriate in the scope of routing and must then be instantiated, the system uses the ControllerLoader to do this. In this context, one can then also characterise a controller as a service.
This procedure of the specialised Sub-ServiceManager has several advantages for certain types of services. For example, in this manner the central ServiceManager for the application services is itself not overloaded with innumerable services, and it is easy to determine all of the controllers, a task that would otherwise not be nearly as easy. Here are the different ServiceManagers again at a glance:
Application Services (Zend\ServiceManager\ServiceManager): Configuration via the service_manager key or the getServiceConfig() method (defined in the ServiceProviderInterface).
Controllers (Zend\Mvc\Controller\ControllerManager): Configuration via controllers key or getControllerConfig() method (defined in ControllerProviderInterface). It can be obtained in the "central ServiceManager" via the ControllerLoader service designation.
Controller plugins (Zend\Mvc\Controller\PluginManager): Configuration via the controller_plugins key or getControllerPluginConfig() method (defined in ControllerPluginProviderInterface). It can be obtained in the "central ServiceManager" via the ControllerPlugin manager service designation.
"View helpers" (Zend\View\HelperPluginManager): configuration via the view_helpers key or the getViewHelperConfig() method.
(defined in the ViewHelperProviderInterface). It can be obtained in the "central ServiceManager" via the ViewHelperManager service designation.
Loading the modules
After the ModuleManager has been prepared and the required listeners have been registered, the actual loading of the modules is initiated by invocation of the loadModules()method of the ModuleManager. At this time, relatively little really happens here because the actual processing, for example the invocation of the above-mentioned methods of the Module.php, indeed occurs in the many registered listeners. Initially, the loadmodules.pre event is triggered, and then the loadModule.resolve event and loadModule, for every activated module. Finally, the loadModules.postevent is again triggered. And that was really everything.
The Module Event Object
The concept of the EventManager is that in addition to being the trigger for an event and the listeners registered for the event (receivers), the event itself—represented as independent object—still exists. It is made available to all listeners. This object serves to transfer additional event-relevant information, for example a reference to the location in the code where the event is triggered. Moreover, additional data, which are helpful for the event processing in the listeners, can be transferred Thus, the module which is now being loaded is generally of interest for a listener during loadModule.
To do justice to the fact that, depending on the context of the event, other data are of interest, the EventManager of Zend Framework 2 permits deposition of situation-dependent special event classes. For the event principle in the scope of the ModulManager, there is as special ModuleEvent class, which for example bears both the module in question and additionally the name of the module.
Activation of a module
In order for a module to be taken into account at all, an explicit activation in application.config.php in the config directory is required:
<?php
return array(
'modules' => array(
'Application',
'Helloworld'
)
);
Listing 6.21
Methods of the module class
As we have seen, a large number of methods in the Module class of a module are invoked if we have implemented them. In the Framework there are two relevant possibilities of finding out whether this is the case. Either the Module class implements a specific interface (this can indeed be tested via instance of) or the respective method is simply implemented (this can be checked via themethod_exists()` invocation).
Let’s now again take a detailed look at the methods in the Module class, which are automatically invoked by the Framework and can be used by application developers:
getAutoloadingConfig() (defined in AutoloaderProviderInterface): We have already created this method in our Helloworld module. It provides information on how the classes of the module can be automatically loaded. If we omit this method, the classes of the module (for example its controller) normally cannot be loaded and serious problems occur when the corresponding URL is invoked. Consequently, it should always be ensured that information on how the classes can be automatically loaded has been made available to the Framework. Incidentally, in a purely technical context, the Framework takes the information to incorporate an appropriate loader implementation for this module via spl_autoload_register().
init() (defined in InitProviderInterface): This method allows the application developer to initialise his or her own module, thus, for example, to register his or her own listeners for certain events. If necessary, the ModuleManager is consigned to the method and the latter can thus access the appropriate events (of the ModuleManager) or access the modules. The important thing is that this "method" is always invoked, that means for every request—and indeed for every module. One should also realize that this is a good place to ruin the loading time of an application. Thus, only very few and ideally only light-weight operations should be performed in the scope of the init() method. If one is attempting to improve the speed of a ZF2 application, one should always first take a look at the init() methods of the activated modules.
Here is an example for the use of the init()method:
<?php
namespace Helloworld;
use Zend\ModuleManager\ModuleManager;
use Zend\ModuleManager\ModuleEvent;
class Module
{
public function init(ModuleManager $moduleManager)
{
$moduleManager->getEventManager()
->attach(
ModuleEvent::EVENT_LOAD_MODULES_POST,
array($this, 'onModulesPost')
);
}
public function onModulesPost()
{
die("Modules loaded!");
}
// [..]
}
Listing 6.21
onBoostrap() (defined in BootstrapListenerInterface): An additional option for the application developer to implement module-specific bootstrapping. Fundamentally, this method has the same purpose and utility as init(), but the onBootstrap is invoked later in the processing; namely, when the ModuleManager has already finished its work and has turned the rudder over to the Application. Thus, when using the onBootstrap(), method, services and data which were not yet accessible in the init() are available.
getConfig() (defined in ConfigProviderInterface): We are also already familiar with this method. It provides the possibility of referring to the module-specific configuration file, which according to convention is termed module.config.php and is deposited in this module in the config subdirectory. However, this is not obligatory. Strictly speaking, this method is absolutely required in order for a module to be executable, but, in practice, one cannot get along without a module-specific configuration file, which one makes accessible to the Framework via this module. With regard to configuration, the Framework allows a certain amount of flexibility. Thus, either all configurations can be made available in one or more external files via getConfig() or special "Config methods" can be implemented in the Module class. The latter refer to the "ServiceManagers" that are present in the system, i.e. to the ServiceManager, ControllerLoader, ViewHelperManager and ControllerPluginManager.
getServiceConfig(): Allows the configuration of the ServiceManager and is equivalent to the "config array key" service_manager in module.config.php.
getControllerConfig(): Allows the configuration of the ControllerLoader and is equivalent to the "config array key" controllers in module.config.php.
getControllerPluginConfig(): Allows the configuration of the ControllerPluginManager and is equivalent to the "config array key" controller_plugins in module.config.php.
getViewHelperConfig(): Allows the configuration of the ViewHelperManager and is equivalent to the "config array key" view_helpers in module.config.php.
In this context another example: our own ViewHelper (more on the concept of the "view helper" on the following pages) can either be made known in the scope of the module.config.php as follows
<?php
'view_helpers' => array(
'invokables' => array(
'displayCurrentDate'
=> 'Helloworld\View\Helper\DisplayCurrentDate'
)
)
Listing 6.22
or in the Module.php with the aid of the appropriate method:
<?php
public function getViewHelperConfig()
{
return array(
'invokables' => array(
'displayCurrentDate'
=> 'Helloworld\View\Helper\DisplayCurrentDate'
)
);
}
Listing 6.23
Application
Now, where the ServiceManager has been equipped with the required services, and the ModuleManager has loaded the application’s modules, the Application itself can be started and the request processing, initiated. This occurs in 3 steps, partly in the init() method of the application itself and partly in the index.php: Starting the application (bootstrap()), followed by the execution (run()) and last but not least returning the generated results (send()):
<?php
$application = $serviceManager->get('Application');
$application->bootstrap()
$application->run();
$application->send();
Listing 6.24
Generation of the application & bootstrapping
The application object is generated via the factory that is registered in the ServiceManager. In the scope of the generation, the Application invokes a number of standard services, among them both the Request, as the basis for further processing, and the Response, which is to be filled with life in the scope of the processing. In addition, the Application gets a reference to the ModuleManager and its own instance of the EventManager (which is indeed deposited in the ServiceManager such that it is not shared; thus a new instance of this service is returned). The latter ensures—analogously to the ModuleManager—that the Application can trigger events and inform listeners. The Application sets off a number of events in the scope of the processing.
bootstrap: Is executed when the application is started.
route: Occurs when a controller and an action are determined for the URL.
dispatch: Takes place when a determined controller is identified and invoked.
render: Occurs when the result for the return is prepared on the basis of templates.
finish: Is executed when the application has been completed.
In the course of bootstrapping the Application likewise registers (incidentally in this case the Application itself and not the ApplicationFactory) a number of listeners, which it also obtains via the ServiceManager: RouteListener for the route event, DispatchListener for the dispatch event and the ViewManager for a colourful bunch of additional listeners, which perform the processing of templates and layouts. More about this in the next section.
Furthermore, the Application then creates the MVC-specific event object (MvcEvent), which is registered with the EventManager as event object. MvcEvent then enables the listeners to access Request, Response, Application and the Router.
Finally, the bootstrap event is initiated and the registered listeners are run. They can also particularly be the application’s individual modules, which have registered for just this event.
Execution
The run() method, which is executed subsequent to bootstrapping, then actuates a number of "levers" that had already been placed in the correct position. To begin with, the Application triggers the route event. The RouteListener, which was registered for this event beforehand, is run and the Router from the MvcEvent is asked to perform its services: i.e. to match the URL to a defined route. In this case, a route is the description of a URL on the basis of a defined pattern. We will take a detailed look at the mechanics of routing later. At the moment, we only have to remember that the Router now either finds a route that fits the invoked URL and thus determines the appropriate controller as well as the appropriate action or, on the contrary, the Router returns with bad news and did not turn up any search results at all. But let’s initially remain on the successful path in this case. The appropriate route is deposited in the form of a RouteMatch object in MvcEvent by the RouteListener. MvcEvent is thus increasingly proving to be a central object in which a number of other important objects and data are available. Then the dispatch event is initiated, the ControllerLoader is invoked and uses the MvcEvent to find the identified controller, which is to be instantiated. To achieve this, the DispatchListener requests the ControllerLoader from the ServiceManager (which, technically speaking, in its own right is also again a "ServiceManager") and then the actual controller from it (i.e. from the ControllerLoader). In the course of this, the controller is also equipped with an EventManager of its own. Now, it can thus actuate events and manage listeners.
Then the controller’s dispatch() method is invoked, and performs the further processing itself. If any intermittent problems occur in this enterprise, the dispatch.error event is triggered; otherwise, the result of the dispatch() invocation is deposited in MvcEvent and additionally returned and thereupon the dispatch process within the controller is concluded.
In Zend Framework, controllers are so conceived that they only have to have one dispatch() method at their disposal. This is externally invoked, in the process the Requestobject is transferred, and the controller is expected to return an object of the Zend\Stdlib\ResponseInterface type when the work has been completed. In order for the principle of controller and action to function, as one is accustomed to and expects, this logic must be implemented in the controller itself; otherwise only the dispatch method would be invoked, but not the appropriate "action" method. To insure that that one does not have to do this oneself, one’s own controller inherits this from the Zend\Mvc\Controller\AbstractActionController. Subsequently, any arbitrary actions can be deposited in the controller when one adheres to the convention that the method name must end with "action":
<?php
namespace Helloworld\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class IndexController extends AbstractActionController
{
private $greetingService;
public function indexAction()
{
return new ViewModel(
array(
'greeting' => $this->greetingService->getGreeting(),
'date' => $this->currentDate()
)
);
}
}
Listing 6.25
Back in the Application , the two results render and finish are now initiated and the run() method concluded. Incidentally, the following fact is very interesting and helpful.: Normally, an action returns an object of the ViewModel type at the end of processing. It thus implicitly signals the subsequent processing steps that the result must still be processed before it can be returned.
<?php
public function indexAction()
{
return new ViewModel(
array(
'greeting' => $this->greetingService->getGreeting(),
'date' => $this->currentDate()
)
);
}
Listing 6.26
However, a very practical implementation detail is the fact that when a Response object is returned instead of a ViewModel, the downstream render activities are omitted.
<?php
public function indexAction()
{
$resp = new \Zend\Http\PhpEnvironment\Response;
$resp->setStatusCode(503);
return $resp;
}
Listing 6.27
This mechanism is helpful if one desires, for example, to briefly return a 503 code, because the application is just undergoing scheduled maintenance or when one desires to return data of a specific Mime type, for example the contents of an image, of a PDF document or something similar.
ViewManager
Before the processing has ended, the ViewManager comes into play again. In the previous section, we have already seen that the onBootstrap() method is executed in the scope of the bootstrapping of the Application (because it is registered for the corresponding event) and that an entire series of additional preparations are made there. Up to now, we have blended this out for simplicity’s sake, but now we also have to look at the details in this case. After the ViewManager with its many collaborators has completed its work, we have actually worked our way through the entire processing chain once.
To begin with, the ViewManager obtains the Config from the ServiceManager, which at this time already represents the merged "total configuration" of the application and of the modules. The ViewManager looks for the view_manager key and uses the configurations deposited there. Then the old game of registering diverse listeners and the provision of additional services begins again.
View-oriented listeners
The following listeners are generated and all of them are attached to the dispatch event of the ActionController class (or more exactly: of the EventManager of the ActionController):
CreateViewModelListener: Ensures that, after execution of the controller, an object of the ViewModel type is available for the rendering, even if only NULL or an array was made available by the controller.
RouteNotFoundStrategy: Generates a ViewModel for the case that no controller was determined and no "View Model" could be generated (404 error).
InjectTemplateListener: Adds the appropriate template to the ViewModel for subsequent rendering.
InjectViewModelListener: Adds the ViewModel to MvcEvent. This listener is also registered for the dispatch.error event of the Application in order to also be able to make a ViewModel available in case of error.
The dispatch event is incidentally somewhat nasty: it occurs twice in the system It is once triggered by the Application, and again by the ActionController. Even if the designation of the event is identical (it can indeed be freely selected), due to the fact that it is triggered by different EventManagers, we are dealing with two completely different events.
Incidentally, at this time, the EventManager of the respective , specific manifestation of the ActionController does not yet exist because the latter has not yet been generated at all. In this case, this problem is avoided by using the SharedEventManager. With the aid of the SharedEventManager, listeners for the events of an EventManager, which does not even exist at the time of registration, can be registered. For the time being, we’ll simple leave things as they are. We’ll take a more detailed look at how this mechanism is realized in the next chapter.
View-oriented services
In addition, a number of view-oriented services are made available in the ServiceManager:
View and View Model: To begin with there is the View itself with its View Model, the representation of the "payload" generated from a request for the response.
DefaultRenderingStrategy: Can access the View and is registered for the render Event of the Application. If this event occurs, the View is transferred to the ViewModel, which is obtained from the MvcEvent and then prompts the View to render just that.
ViewPhpRendererStrategy: However, the actual rendering is not performed by the Viewitself, but is instead delegated to the ViewPhpRendererStrategy, which initially specifies the appropriate renderer and transfers the finished result to theResponse` subsequent to processing.
RouteNotFoundStrategy: Defines the appropriate behaviour in case of a 404 situation.
ExceptionStrategy: Defines the appropriate behaviour in case of a "dispatch errors".
ViewRenderer: Takes over the actual rendering work, i.e. the merging of the ViewModel data and the appropriate template.
ViewResolver: In order to localise the respective template, the ViewRenderer accesses the ViewResolver.
ViewTemplatePathStack: Makes it possible for the "resolver" to localise a template on the basis of deposited paths.
ViewTemplateMapResolver: Makes it possible for the "resolver" to localise a template on the basis of a "key value assignment".
ViewHelperManager: Makes it possible to access "ViewHelpers" in templates; this simplifies the generation of dynamic markup.
In case of an error in the scope of routing or in the course of dispatching, respectively, the Application triggers a dispatch.error event. This signalises that an error has occurred in the processing.
Summary
At first glance, the relationships appear very complex; the implementation of the MVC pattern in Zend Framework initially feels somehow over-engineered. The main reason for this feeling is the fact that, on the one hand, the entire processing procedure is broken down into extremely small individual steps, which are represented via individual classes in each case, and which must also be chronologically and contentually "orchestrated" in order that they also ultimately meaningfully interact—to the extent that is required. On the other hand, the excessive use of events and listeners makes it difficult to understand the processes and relationships within the application. Nor does the use of numerous design patterns exactly contribute to comprehension, particularly in the beginning, especially not when one is not yet accustomed to them.
Isn't it possible to simplify things greatly? Must MVC implementation really always be so complex? Th quick, unreflected answer to these questions would be: Yes. No. There is a large number of so-called "Micro MVC Frameworks", such as Silex or MicroMVC, which at first glance appear to be able to achieve similar results with significantly less complexity than the MVC implementation in Zend Framework 2. However, this is only true at first glance.
To begin with, Zend\Mvc is actually much more than "MVC". It is in reality an application platform that allows
1) the simple and effective integration of additional function in the form of one's own or third party modules,
2) the modification or complete restructuring of request processing in nearly any arbitrary manner, and
3) which, as a result of its loose coupling approach to individual components and services, also allows fulfilment of the requirements of company applications with regard to maintainability and extensibility as well as testability.
Zend\Mvc achieves an environment of software components and is able elevate the abstraction level, on which an earlier application developer moved about in the development of web applications with PHP, to a new, substantially more productive one. At least, it is beginning to achieve just that. Whether this will really succeed must be proven. Is Zend Framework 2 the right software for my project? I think that this question is more difficult to answer for the Version 2 than it was for Version 1. The advantages of Version 2 are particularly aimed at the profession application, which does not always exist. Is Zend Framework 2 the right software for my company application in the web? Yes, I would categorically say that in any case.
Let’s summarise this chapter and the course of request processing again briefly. To begin with, the request lands at the index.php via the use of URL rewriting (for example, via "mod_rewrite" and the appropriate .htaccess file). The autoloading is configured there, which thus ensures that both Zend Framework itself, but also, if need be, additionally used libraries function properly. Subsequently, the Application is started, which initially ensures that the ServiceManager, the central "service access", is generated and equipped with important services: the ModuleManager and the EventManager. Furthermore, the SharedEventManageris made available As in the case of the ModuleManager and the EventManager, made available frequently means that to begin with only the factories, which are consulted for the subsequent generation of actual services in each case, are made known. Why should we detour via factories? On the one hand, because they allow us to exchange the specific implementation of the respective service if necessary. And on the other hand, because in the scope of service generation, not only the service itself is generated, but also a number of listeners, which are registered for the subsequent processing events, as is the case for the ModuleManager. Indeed, the ModuleManagerFactory, ApplicationFactory and the ViewManager perform in a similar manner in the generation of services. They initially generate the actual service, then a number of additional (sub-)services, which the service will subsequently access, and finally one or more listeners for the events of one’s own or other services. The listeners then take over a specific task themselves or refer back to the services.
This procedure ensures that the methods run, loadModules, bootstrap, & co. of the ModuleManager, Application & co. are very lean and really don’t do anything themselves other than to trigger the events. Everything else then passes via the listeners to the services. During its execution, the ModuleManager initially triggers the following events: loadModules.pre, the loadModule.resolve, the loadModule and the loadModules.post. Subsequently, the Application sets off the bootstrap, route and dispatchevents; subsequently the controller with its own dispatch event (not to be confused with that of the Application); finally the Application again with render, before the View reports with renderer and response. Last but not least, the Application ends the firework event with finish. In case of an error in the scope of routing or in the course of dispatching, respectively, the Application triggers a dispatch.error event. This signalises that a problem has occurred in process and that the appropriate error treatment has been activated.