Você está na página 1de 414

Using Zend Framework 2

The first book about Zend Framework 2 easy to read


and understand for beginners
Oleg Krivtsov
This book is for sale at http://leanpub.com/using-zend-framework-2
This version was published on 2014-04-15

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools
and many iterations to get reader feedback, pivot until you have the right book and build
traction once you do.
2013 - 2014 Oleg Krivtsov

To my father who assembled my first computer and shown me how to write a simple program.
To my mother who shown me how to overcome lifes obstacles and become a winner.

Contents
Preface . . . . . . . . . . . . .
About this Leanpub Book . .
Why to Read this Book? . .
Zend Framework Explained
See ZF2 Wider . . . . . . . .
ZF2 Book for Beginners . . .
Structure of the Book . . . .
Learn ZF2 by Example . . .
Book Site . . . . . . . . . . .
Your Feedback . . . . . . . .
Affiliate Program . . . . . .
Acknowledgements . . . . .

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

i
i
i
ii
ii
ii
iii
iii
iv
v
v
vi

1 Introduction to Zend Framework 2 . . . . . . .


1.1 What is Zend Framework 2? . . . . . . . . .
1.2 License . . . . . . . . . . . . . . . . . . . . .
1.3 What Companies Prefer Zend Framework 2?
1.4 Release Schedule . . . . . . . . . . . . . . .
1.5 Distributions . . . . . . . . . . . . . . . . . .
1.6 User Support . . . . . . . . . . . . . . . . . .
1.7 Supported Operating Systems . . . . . . . .
1.8 Server Requirements . . . . . . . . . . . . .
1.9 Security . . . . . . . . . . . . . . . . . . . .
1.10 Performance . . . . . . . . . . . . . . . . . .
1.11 Design Patterns . . . . . . . . . . . . . . . .
1.12 Components . . . . . . . . . . . . . . . . . .
1.13 ZF2 Service Components . . . . . . . . . . .
1.14 Differences with Zend Framework 1 . . . . .
1.14.1 Backwards Compatibility . . . . . . .
1.14.2 ZFTool . . . . . . . . . . . . . . . . .
1.14.3 Modules . . . . . . . . . . . . . . . . .
1.14.4 Aspect Oriented Design . . . . . . . .
1.14.5 Namespaces . . . . . . . . . . . . . . .
1.14.6 Configuration . . . . . . . . . . . . . .
1.14.7 Service Manager . . . . . . . . . . . .
1.15 Competing Frameworks . . . . . . . . . . .
1.16 Summary . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

1
1
2
3
5
6
6
8
8
9
10
10
11
15
16
16
17
17
17
17
17
17
18
21

CONTENTS

2 Zend Skeleton Application . . . . . . . . . . . . .


2.1 Getting Zend Skeleton Application . . . . . . .
2.2 Typical Directory Structure . . . . . . . . . . .
2.3 Installing Dependencies with Composer . . . .
2.4 Apache Virtual Host . . . . . . . . . . . . . .
2.5 Opening the Web Site in Your Browser . . . .
2.6 Creating NetBeans Project . . . . . . . . . . .
2.7 Hypertext Access File (.htaccess) . . . . . . . .
2.8 Blocking Access to the Web Site by IP Address
2.9 HTTP Authentication . . . . . . . . . . . . . .
2.10 Having Multiple Virtual Hosts . . . . . . . . .
2.11 Hosts File . . . . . . . . . . . . . . . . . . . .
2.12 Advanced Composer Usage . . . . . . . . . . .
2.12.1 Package Names and Versions . . . . . .
2.12.2 Installing and Updating Packages . . . .
2.12.3 Virtual Packages . . . . . . . . . . . . .
2.12.4 Composer and Version Control Systems
2.13 Summary . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

22
22
23
25
27
29
30
32
34
35
36
36
37
37
38
39
40
41

3 Web Site Operation . . . . . . . . . . . . . . . . . .


3.1 PHP Namespaces . . . . . . . . . . . . . . . . .
3.2 PHP Interfaces . . . . . . . . . . . . . . . . . . .
3.3 PHP Class Autoloading . . . . . . . . . . . . . .
3.4 PSR-0 Standard . . . . . . . . . . . . . . . . . .
3.5 HTTP Request and Response . . . . . . . . . . .
3.6 Site Entry Script . . . . . . . . . . . . . . . . . .
3.7 Events & Applications Life Cycle . . . . . . . .
3.8 Application Configuration . . . . . . . . . . . .
3.8.1 Application-Level Config Files . . . . . . .
3.8.2 Application-Level Extra Config Files . . .
3.8.3 Module-Level Config Files . . . . . . . . .
3.8.4 Combining the Configuration Files . . . .
3.9 Module Entry Point . . . . . . . . . . . . . . . .
3.10 Class Autoloading in Zend Framework 2 . . . .
3.10.1 Composer-provided Autoloader . . . . . .
3.10.2 Zend\Loader Component . . . . . . . . .
3.10.2.1 Standard Autoloader . . . . . . .
3.10.2.2 PSR-0 and Src Directory Structure
3.10.2.3 Class Map Autoloader . . . . . .
3.11 Service Manager . . . . . . . . . . . . . . . . . .
3.11.1 Service Locator . . . . . . . . . . . . . . .
3.11.2 Canonical Service Names . . . . . . . . .
3.11.3 Registering a Service . . . . . . . . . . . .
3.11.4 Registering Invokable Classes . . . . . . .
3.11.5 Registering Factories . . . . . . . . . . . .
3.11.6 Registering Service Aliases . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

42
42
44
46
47
49
50
51
52
53
55
55
56
57
58
58
59
60
61
62
62
64
65
65
67
67
68

CONTENTS

3.11.7 Service Manager Configuration . . . . . . . . . . . . . . . . . . . . . . . .


3.12 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4 Model-View-Controller . . . . . . . . . . . . . . .
4.1 Get the Hello World Example from GitHub . .
4.2 Separating Business Logic from Presentation .
4.3 Controllers . . . . . . . . . . . . . . . . . . . .
4.3.1 Base Controller Class . . . . . . . . . . .
4.3.2 Retrieving Data from HTTP Request . .
4.3.3 Retrieving GET or POST Variables . . .
4.3.4 Putting Data to HTTP Response . . . . .
4.4 Variable Containers . . . . . . . . . . . . . . .
4.5 Controller Registration . . . . . . . . . . . . .
4.6 When to Create a New Controller? . . . . . . .
4.7 Controller Plugins . . . . . . . . . . . . . . . .
4.7.1 Writing Own Controller Plugin . . . . .
4.8 Views . . . . . . . . . . . . . . . . . . . . . .
4.9 View Helpers . . . . . . . . . . . . . . . . . .
4.10 View Template Names & View Resolver . . . .
4.11 Disabling the View Rendering . . . . . . . . .
4.12 Error Pages . . . . . . . . . . . . . . . . . . .
4.13 Models . . . . . . . . . . . . . . . . . . . . . .
4.14 Model Types . . . . . . . . . . . . . . . . . . .
4.14.1 Entities . . . . . . . . . . . . . . . . . .
4.14.2 Value Objects . . . . . . . . . . . . . . .
4.14.3 Services . . . . . . . . . . . . . . . . . .
4.14.4 Factories . . . . . . . . . . . . . . . . .
4.14.5 Repositories . . . . . . . . . . . . . . . .
4.15 Determining the Correct Model Type . . . . .
4.16 Skinny Controllers, Fat Models, Simple Views .
4.16.1 Skinny Controllers . . . . . . . . . . . .
4.16.2 Fat Models . . . . . . . . . . . . . . . .
4.16.3 Simple View Templates . . . . . . . . . .
4.17 Summary . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

69
70

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

72
72
73
75
77
78
79
80
80
82
83
83
84
85
89
91
93
95
97
97
98
99
100
102
102
103
104
104
105
106
106

5 URL Routing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.1 URL Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2 Route Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.3 Combining Route Types . . . . . . . . . . . . . . . . . . . . . . .
5.3.1 Simple Route Stack . . . . . . . . . . . . . . . . . . . . . . .
5.3.2 Tree Route Stack . . . . . . . . . . . . . . . . . . . . . . . .
5.4 Routing Configuration . . . . . . . . . . . . . . . . . . . . . . . .
5.4.1 Configuration for Simple Routes . . . . . . . . . . . . . . . .
5.4.2 Configuration for Nested Routes . . . . . . . . . . . . . . .
5.4.3 Default Routing Configuration in Zend Skeleton Application
5.5 Literal Route Type . . . . . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

107
107
108
109
110
110
111
112
112
113
115

CONTENTS

5.6
5.7
5.8
5.9
5.10

Segment Route Type . . . . . . . . . . . . . . . . . . .


Regex Route Type . . . . . . . . . . . . . . . . . . . . .
Wildcard Route Type . . . . . . . . . . . . . . . . . . .
Other Route Types . . . . . . . . . . . . . . . . . . . .
Extracting Parameters from Route . . . . . . . . . . . .
5.10.1 Retrieving the RouteMatch and the Router Object
5.11 Generating URLs from Route . . . . . . . . . . . . . . .
5.11.1 Generating URLs in View Templates . . . . . . .
5.11.1.1 Passing Parameters . . . . . . . . . . . .
5.11.1.2 Generating Absolute URLs . . . . . . . .
5.11.1.3 Specifying Query Part . . . . . . . . . .
5.11.2 Generating URLs in Controllers . . . . . . . . . .
5.11.3 URL Encoding . . . . . . . . . . . . . . . . . . .
5.12 Writing Own Route Type . . . . . . . . . . . . . . . . .
5.12.1 RouteInterface . . . . . . . . . . . . . . . . . . .
5.12.2 Custom Route Class . . . . . . . . . . . . . . . .
5.13 Summary . . . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

116
120
122
124
126
127
128
128
129
130
131
131
132
133
133
133
143

6 Page Appearance and Layout . . . . . . . . . . . . . . .


6.1 About CSS Stylesheets and Twitter Bootstrap . . . .
6.2 Page Layout in Zend Framework 2 . . . . . . . . . .
6.3 Modifying the Default Page Layout . . . . . . . . .
6.4 Switching between Layouts . . . . . . . . . . . . . .
6.4.1 Setting Layout for All Actions of a Controller
6.5 Partial Views . . . . . . . . . . . . . . . . . . . . .
6.6 Placeholder View Helper . . . . . . . . . . . . . . .
6.7 Adding Scripts to a Web Page . . . . . . . . . . . .
6.7.1 Example . . . . . . . . . . . . . . . . . . . . .
6.8 Adding CSS Stylesheets to a Web Page . . . . . . .
6.8.1 Example . . . . . . . . . . . . . . . . . . . . .
6.9 Writing Own View Helpers . . . . . . . . . . . . . .
6.9.1 Menu . . . . . . . . . . . . . . . . . . . . . .
6.9.2 Breadcrumbs . . . . . . . . . . . . . . . . . .
6.10 View Models and Page Composition . . . . . . . . .
6.11 Summary . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

144
144
146
150
154
155
156
158
160
164
167
168
169
171
177
179
181

7 Collecting User Input with Forms . . . . . . . . .


7.1 Get the Form Demo Sample from GitHub . . .
7.2 About HTML Forms . . . . . . . . . . . . . .
7.2.1 Fieldsets . . . . . . . . . . . . . . . . . .
7.2.2 Example: Contact Us Form . . . . . . .
7.2.3 GET and POST Methods . . . . . . . . .
7.3 Styling HTML Forms with Twitter Bootstrap .
7.4 Retrieving Form Data in a Controllers Action
7.5 Forms and Model-View-Controller . . . . . . .
7.5.1 A Typical Form Usage Workflow . . . .

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

183
183
184
186
187
188
189
191
194
195

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

CONTENTS

7.6
7.7

7.8
7.9

7.10
7.11
7.12

7.13

7.14

A Form Model . . . . . . . . . . . . . . . . . . . . . . . . . . .
Form Elements . . . . . . . . . . . . . . . . . . . . . . . . . .
7.7.1 Adding Elements to a Form Model . . . . . . . . . . . .
7.7.2 Method 1: Passing an Instance of an Element . . . . . . .
7.7.3 Method 2: Using Array Specification . . . . . . . . . . .
Example: Creating the Contact Form Model . . . . . . . . . . .
Adding Form Validation Rules . . . . . . . . . . . . . . . . . .
7.9.1 Input Filter . . . . . . . . . . . . . . . . . . . . . . . . .
7.9.2 Adding Inputs to Input Filter . . . . . . . . . . . . . . .
7.9.2.1 Filter Configuration . . . . . . . . . . . . . . . .
7.9.2.2 Validator Configuration . . . . . . . . . . . . .
7.9.2.3 Attaching Input Filter to Form Model . . . . . .
7.9.3 Creating Input Filter for the Contact Form . . . . . . . .
Using the Form in a Controllers Action . . . . . . . . . . . . .
7.10.1 Passing Form Data to a Model . . . . . . . . . . . . . . .
Form Presentation . . . . . . . . . . . . . . . . . . . . . . . . .
7.11.1 Preparing the Form Model for Rendering . . . . . . . . .
Standard Form View Helpers . . . . . . . . . . . . . . . . . . .
7.12.1 Rendering a Form Element . . . . . . . . . . . . . . . . .
7.12.2 Rendering an Elements Validation Errors . . . . . . . .
7.12.3 Rendering an Elements Label . . . . . . . . . . . . . . .
7.12.4 Rendering a Form Row . . . . . . . . . . . . . . . . . . .
7.12.5 Rendering the Entire Form . . . . . . . . . . . . . . . . .
Example: Creating the View Template for the Contact Form . .
7.13.1 Applying the Bootstrap CSS Styles to Form . . . . . . . .
7.13.2 Styling the Validation Errors List . . . . . . . . . . . . .
7.13.3 Adding the Thank You & Error Sending Email Pages
7.13.4 Results . . . . . . . . . . . . . . . . . . . . . . . . . . .
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

8 Transforming Input Data with Filters . . . . . . . . . . . . .


8.1 About Filters . . . . . . . . . . . . . . . . . . . . . . . . .
8.1.1 FilterInterface . . . . . . . . . . . . . . . . . . . . .
8.2 Standard Filters Overview . . . . . . . . . . . . . . . . .
8.3 Instantiating a Filter . . . . . . . . . . . . . . . . . . . . .
8.3.1 Method 1: Instantiating a Filter Manually . . . . . .
8.3.2 Method 2: Constructing a Filter with the StaticFilter
8.3.3 Method 3: Constructing a Filter From Array . . . .
8.4 About Filter Plugin Manager . . . . . . . . . . . . . . . .
8.5 Filters Behaviour in Case of Incorrect Input Data . . . .
8.6 Filter Usage Examples . . . . . . . . . . . . . . . . . . . .
8.6.1 Filters Casting Input Data to a Specified Type . . .
8.6.1.1 Int Filter . . . . . . . . . . . . . . . . . . .
8.6.1.2 Boolean Filter . . . . . . . . . . . . . . . .
8.6.1.3 Null Filter . . . . . . . . . . . . . . . . . .
8.6.1.4 DateTimeFormatter Filter . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

196
198
201
201
203
204
208
208
209
210
210
211
211
214
218
222
222
223
224
225
225
226
227
227
229
231
232
232
235

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

236
236
236
237
239
240
242
242
243
243
244
244
244
244
247
248

CONTENTS

8.6.2 Filters Performing Manipulations on a File Path . . . . . . . .


8.6.2.1 BaseName Filter . . . . . . . . . . . . . . . . . . . . .
8.6.2.2 Dir Filter . . . . . . . . . . . . . . . . . . . . . . . . .
8.6.2.3 RealPath Filter . . . . . . . . . . . . . . . . . . . . . .
8.6.3 Filters Performing Compression and Encryption of Input Data
8.6.3.1 Compress Filter . . . . . . . . . . . . . . . . . . . . .
8.6.3.2 Encrypt Filter . . . . . . . . . . . . . . . . . . . . . .
8.6.4 Filters Manipulating String Data . . . . . . . . . . . . . . . . .
8.6.4.1 StringToLower Filter . . . . . . . . . . . . . . . . . .
8.6.4.2 PregReplace Filter . . . . . . . . . . . . . . . . . . . .
8.6.4.3 StripTags Filter . . . . . . . . . . . . . . . . . . . . .
8.6.4.4 StripNewlines Filter . . . . . . . . . . . . . . . . . . .
8.6.4.5 UriNormalize Filter . . . . . . . . . . . . . . . . . . .
8.6.5 Organizing Filters in a Chain . . . . . . . . . . . . . . . . . .
8.6.6 Custom Filtering with the Callback Filter . . . . . . . . . . . .
8.6.6.1 Example . . . . . . . . . . . . . . . . . . . . . . . . .
8.7 Writing Own Filter . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.7.1 Using the PhoneFilter Class . . . . . . . . . . . . . . . . . . .
8.8 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

249
249
249
250
251
251
253
254
254
255
256
257
257
259
260
261
264
267
268

9 Checking Input Data with Validators . . . . . . . . . . . . . . . . . . . . .


9.1 About Validators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9.1.1 ValidatorInterface . . . . . . . . . . . . . . . . . . . . . . . . . .
9.2 Standard Validators Overview . . . . . . . . . . . . . . . . . . . . . . .
9.3 Validator Behaviour in Case of Invalid or Unacceptable Data . . . . . .
9.4 Instantiating a Validator . . . . . . . . . . . . . . . . . . . . . . . . . .
9.4.1 Method 1. Manual Instantiation of a Validator . . . . . . . . . . .
9.4.2 Method 2. Using StaticValidator Wrapper . . . . . . . . . . . . . .
9.4.3 Method 3. Using an Array Configuration . . . . . . . . . . . . . .
9.5 About Validator Plugin Manager . . . . . . . . . . . . . . . . . . . . . .
9.6 Validator Usage Examples . . . . . . . . . . . . . . . . . . . . . . . . .
9.6.1 Validators for Checking Value Conformance to Certain Format . .
9.6.1.1 Ip Validator . . . . . . . . . . . . . . . . . . . . . . . . .
9.6.1.2 Hostname Validator . . . . . . . . . . . . . . . . . . . . .
9.6.1.3 Uri Validator . . . . . . . . . . . . . . . . . . . . . . . .
9.6.1.4 Date Validator . . . . . . . . . . . . . . . . . . . . . . . .
9.6.1.5 Regex Validator . . . . . . . . . . . . . . . . . . . . . . .
9.6.2 Validators for Checking a Numerical Value Lies in a Given Range
9.6.2.1 NotEmpty Validator . . . . . . . . . . . . . . . . . . . . .
9.6.2.2 Between Validator . . . . . . . . . . . . . . . . . . . . .
9.6.2.3 InArray Validator . . . . . . . . . . . . . . . . . . . . . .
9.6.2.4 StringLength Validator . . . . . . . . . . . . . . . . . . .
9.6.3 Organizing Validators in a Chain . . . . . . . . . . . . . . . . . .
9.6.4 Custom Validation with the Callback Validator . . . . . . . . . . .
9.6.4.1 Example . . . . . . . . . . . . . . . . . . . . . . . . . . .
9.7 Writing Own Validator . . . . . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

270
270
270
271
273
274
275
278
279
280
280
280
280
281
283
284
285
286
286
287
288
290
291
292
292
294

CONTENTS

9.8
9.9

9.7.1 Using the PhoneValidator Class . . . . . . . . . . . . . . . . . . . . . . . . 298


Using Filters & Validators Outside a Form . . . . . . . . . . . . . . . . . . . . . . 300
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301

10 Advanced Usage of Forms . . . . . . . . . . . . . . . . . . . . . . . . . .


10.1 Form Security Elements . . . . . . . . . . . . . . . . . . . . . . . . .
10.1.1 CAPTCHA . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10.1.1.1 CAPTCHA Types . . . . . . . . . . . . . . . . . . . .
10.1.1.2 CAPTCHA Form Element & View Helper . . . . . . .
10.1.2 Example 1: Adding Image CAPTCHA to the ContactForm . . .
10.1.3 Example 2: Adding a FIGlet CAPTCHA to the ContactForm .
10.1.4 Example 3: Adding reCaptcha CAPTCHA to the ContactForm
10.1.5 CSRF Prevention . . . . . . . . . . . . . . . . . . . . . . . . .
10.1.5.1 Example: Adding a CSRF Element to Form . . . . . .
10.2 Uploading Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10.2.1 HTTP Binary Transfer Encoding . . . . . . . . . . . . . . . .
10.2.2 $_FILES Super-Global Array in PHP . . . . . . . . . . . . . .
10.2.3 Accessing Uploaded Files in ZF2 . . . . . . . . . . . . . . . . .
10.2.4 File Uploads & ZF2 Form Model . . . . . . . . . . . . . . . . .
10.2.5 Validating Uploaded Files . . . . . . . . . . . . . . . . . . . .
10.2.6 Filtering Uploaded Files . . . . . . . . . . . . . . . . . . . . .
10.2.7 InputFilter Container & File Uploads . . . . . . . . . . . . . .
10.2.7.1 FileInput . . . . . . . . . . . . . . . . . . . . . . . . .
10.2.7.2 Executing Validators before Filters . . . . . . . . . . .
10.2.8 Example: Image Gallery . . . . . . . . . . . . . . . . . . . . .
10.2.8.1 Adding ImageForm Model . . . . . . . . . . . . . . .
10.2.8.2 Adding Validation Rules to ImageForm Model . . . .
10.2.8.3 Writing ImageManager Service . . . . . . . . . . . .
10.2.8.4 Adding ImageController . . . . . . . . . . . . . . . .
10.3 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

302
302
302
303
304
305
308
310
312
314
315
315
317
319
319
320
322
323
323
324
325
326
328
330
333
342

Appendix A. Configuring Web Development Environment


Installing Apache, PHP and MySQL in Linux . . . . . . . .
Installing Apache and PHP . . . . . . . . . . . . . . .
Checking Web Server Installation . . . . . . . . . . . .
Editing PHP Configuration . . . . . . . . . . . . . . .
Restarting Apache Web Server . . . . . . . . . . . . .
Enabling Apaches mod_rewrite module . . . . . . . .
Creating Apache Virtual Host . . . . . . . . . . . . . .
Installing XDebug PHP extension . . . . . . . . . . . .
Installing APC PHP Extension . . . . . . . . . . . . .
Installing MySQL Database Server . . . . . . . . . . .
Configuring the MySQL Database Server . . . . . . .
Installing Apache, PHP and MySQL in Windows . . . . . .
Checking Web Server Installation . . . . . . . . . . . .
Enabling Apaches mod_rewrite module . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

343
343
344
346
346
348
348
349
350
352
352
353
353
356
357

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

CONTENTS

Creating Apache Virtual Host . . . . . . .


Installing XDebug PHP extension . . . . .
Installing MySQL Database Server . . . .
Configuring the MySQL Database Server
Installing NetBeans IDE in Linux . . . . . . .
Method 1. Installing from Repository . . .
Method 2. Downloading from Web Site .
Installing NetBeans IDE in Windows . . . . .
Summary . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

358
359
360
360
362
362
363
366
366

Appendix B. Introduction to PHP Development in NetBeans IDE


Run Configuration . . . . . . . . . . . . . . . . . . . . . . . . .
Running the Web Site . . . . . . . . . . . . . . . . . . . . . . . .
Site Debugging in NetBeans . . . . . . . . . . . . . . . . . . . .
Debug Toolbar . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Breakpoints . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Watching Variables . . . . . . . . . . . . . . . . . . . . . . . . .
Call Stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Debugging Options . . . . . . . . . . . . . . . . . . . . . . . . .
Profiling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

368
368
370
371
372
373
374
375
376
378
381

Appendix C. Introduction to Twitter Bootstrap


Overview of Bootstrap Files . . . . . . . . . .
Grid System . . . . . . . . . . . . . . . . . . .
Defining the Grid . . . . . . . . . . . . .
Offsetting Columns . . . . . . . . . . . .
Nesting Grids . . . . . . . . . . . . . . .
Mobile First Concept . . . . . . . . . . .
Bootstraps Interface Components . . . . . . .
Navigation Bar . . . . . . . . . . . . . . .
Dropdown Menu . . . . . . . . . .
Collapsible Navbar . . . . . . . . .
Inverse Navbar Style . . . . . . . .
Breadcrumbs . . . . . . . . . . . . . . . .
Pagination . . . . . . . . . . . . . . . . .
Buttons & Glyphicons . . . . . . . . . . .
Customizing Bootstrap . . . . . . . . . . . . .
Summary . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

382
382
384
385
386
386
387
388
389
389
390
391
392
392
393
395
396

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

About the Author . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397

Preface
About this Leanpub Book
To be fair, this book is a work in progress. Although some chapters of this book have been
finished, the book is still being developed, new chapters are being added, and mistakes are being
fixed, based on your (and other readers) feedback.
Chapter

Status

Chapter 1. Introduction to Zend Framework 2


Chapter 2. Zend Skeleton Application
Chapter 3. Web Site Operation
Chapter 4. Model-View-Controller
Chapter 5. URL Routing
Chapter 6. Page Appearance and Layout
Chapter 7. Collecting User Input with Forms
Chapter 8. Transforming Input Data with Filters
Chapter 9. Checking Input Data with Validators
Chapter 10. Advanced Usage of Forms
Chapter 11. Database Management with Doctrine
Chapter 12. User Management

Completed
Completed
Completed
Completed
Completed
Completed
Completed
Completed
Completed
In progress, 40% ready.
Pending
Pending

Thanks to Leanpub, you have a chance to obtain this book at a lower cost and give your feedback
to the author. This makes it possible for the author to immediately correct mistakes and enhance
the places in text that you point to, thus creating a high-quality product.
This book is a work in progress, and you will receive all newer versions of this book
for free as they appear.

Why to Read this Book?


The Using Zend Framework 2 book is about programming web-sites with Zend Framework 2.
With this e-Book, you can save your time and efforts learning ZF2.
The author strives to give material starting with simple things that a beginner should understand.
Advanced things go last in a chapter. This makes this book the first book about Zend Framework
that is easy to read and understand for a newbie.
You can have a brief introduction to the book by watching the intro video on YouTube!
https://leanpub.com
http://youtu.be/A9BSV0RXn2k

Preface

ii

Zend Framework Explained


The Using Zend Framework 2 book is dedicated to web site development with PHP and
Zend Framework 2 (ZF2). ZF2 is a modern PHP web development framework intended for
building professionally looking, scalable and secure web-sites. Such web sites are easy to test
and maintain. The framework utilizes the best practices and common design patterns, inspired
by the evolution of web development industry. This includes Model-View-Controller pattern,
allowing to organize the code in a consistent and standard way, making it easier to implement
automatic code testing.

See ZF2 Wider


This e-book is not only about Zend Framework, but also about closely related libraries. Although
Zend Framework 2 has dedicated component for accessing the database, in this book we use
third-party library called Doctrine ORM a de-facto standard object-oriented way to perform
database management. We also describe Doctrine-provided database migrations allowing to
apply changes to the database in a standard way. In the sample applications we will create
in chapters of this book, Twitter Bootstrap CSS Framework is used, allowing to produce nice
looking visual appearance and layout of HTML elements on the web pages.

ZF2 Book for Beginners


This book is intended for web developers involved in the development of sites in PHP. The author
strives to give material starting with simple things that a beginner should understand. Advanced
things go last in a chapter. You do not need to be a guru in design patterns to understand most
of the stuff.
To read and understand this book, you need to have a basic knowledge of PHP language. A good
point for learning PHP is its official web site and the online documentation. It would be good
if you have some idea of what is HTTP request, GET and POST variables, namespaces, classes
and interfaces.
Because PHP is closely related to other web technologies, it is also recommended that you have
some basic experience in the following:
HTML (Hyper Text Markup Language) used for creating web pages that can be displayed
in a web browser.
CSS (Cascading Style Sheets) used for defining the look and feel of a web page, like font
size or background color.
JavaScript a client-side scripting language used for making a web page more interactive.
For learning HTML, CSS and JavaScript, a good starting point is W3Schools Tutorials.
http://php.net/
http://php.net/docs.php
http://www.w3schools.com/

Preface

iii

Structure of the Book


This book is divided by chapters. A chapter is dedicated to a single topic. For example, Chapter 1
Introduction to Zend Framework 2 is intended to make you familiar with fundamental concepts
and main components of the framework; Chapter 2 Zend Skeleton Application is dedicated to
giving you instructions to install the skeleton application, which can be used for creation of your
own web sites, and so on.

Learn ZF2 by Example


This ZF2 books text is illustrated with code samples (the source code is published on GitHub).
Each sample is a complete web-site you can install and run yourself to see Zend Framework 2 in
action. You can even use the samples as a base for your own web sites.
All the source code is stored on GitHub code hosting. The code is publicly available, and you
can download the entire code archive by visiting this page. To download the archive, click the
Download ZIP button that can be found on the page (see the figure below).
https://github.com/olegkrivtsov/using-zend-framework-2-book

iv

Preface

Samples can be downloaded from GitHub

The structure of the code archive is presented below.


using-zend-framework-2-book
blog
helloworld
formdemo
...

Book Site
The Using Zend Framework 2 book has a dedicated web site using-zend-framework-2book.com. This is the central place where you can find all the information about the book:
intro videos,
tutorials,
http://using-zend-framework-2-book.com

Preface

code examples,
reader reviews & feedback,
announcements,
and more.

Your Feedback
Thank you for reading this book and helping to make it better. You are encouraged to point
out errors, make suggestions and critical remarks. You can write the author a message through
the dedicated Forum. Alternatively, you can contact the author through his E-mail address
(olegkrivtsov@gmail.com). Your feedback is highly appreciated.

Affiliate Program
Now the Using Zend Framework 2 book is part of the Leanpub Affiliate Program. This means
that anyone can earn 50% of profit for advertising the book on his/her web site. So, if you like this
book and want to earn money by promoting it on your web page or blog, feel free to participate!
How does this Work?
1. Create an account at Leanpub site.
2. Go to the Affiliate page in your account.
3. Look for the Using Zend Framework 2 book in the list of books participating in the
affiliate program.
4. Click the Copy affiliate URL button.
5. Paste the affiliate URL on your web site.
6. When someone buys the book after going to the page using your affiliate code, you get
50% of the minimum price of the book.

Affiliate Link

You can read more about the Leanpub affiliate program terms on this page.
https://leanpub.com/using-zend-framework-2/feedback
http://blog.leanpub.com/2014/03/introducing-the-leanpub-affiliate-program.html
https://leanpub.com/
http://blog.leanpub.com/2014/03/introducing-the-leanpub-affiliate-program.html

Preface

vi

Acknowledgements
Thanks to Edu Torres, a 2D artist from Spain, for making the cover and an illustration for this
book, and for making a design for the books web site. Also thanks to Moriancumer Richard Uy
and Charles Naylor for helping the author to find and fix the mistakes in the text.
The author would like to thank Richard Holloway (an organiser of PHP Hampshire, which is
a recognised PHP user group in South England) for reviewing the book. Richards review is
really useful for determining the proper development direction for this book.

http://phphants.co.uk
http://richardjh.org/blog/book-review-using-zend-framework-2/

1 Introduction to Zend Framework 2


In this chapter well learn about Zend Framework 2, its main principles and components. Well
also compare Zend Framework 2 with other PHP frameworks.

1.1 What is Zend Framework 2?


PHP is a popular web-site development language. However, it has been proven that writing websites in pure PHP is difficult. If you write a web application in PHP, you have to organize your
code in some way, collect and validate user input, implement support of user access control,
manage database, perform scheduled mail delivery, test your code and so on. As your site grows
in size, it becomes more and more difficult to develop the code in such manner. Moreover, when
you switch to the development of a new site, you will notice that a large portion of the code you
have already written for the old site can be used again with small modifications. This code can
be separated in a library. This is how frameworks appeared.
A framework is some kind of a library, a piece of software (also written in PHP) providing
web developers with code base and consistent standardized ways of creating web applications.
Imagine that your web-site is a house, then PHP language is its foundation, and the framework
is the basement. The basement contains a lot of building blocks (components) and tools prepared
for you to make it easier to build the upper floors of your house (see figure 1.1).
Zend Framework 2 is a free and open-source PHP framework. Its development is guided (and
sponsored) by Zend, which is also known as the vendor of the PHP language itself. The first
version (Zend Framework 1) was released in 2007 and since then it has become obsolete. Zend
Framework 2 (or shortly ZF2) is the second version of this software, and it was released in
September 2012. At the moment of writing this book, Zend Framework 2.3 is out.
Zend Framework 2 provides you with the following capabilities:
Develop your web site much faster than when you write it in pure PHP. ZF2 provides
many components that can be used as a code base for creating your site.
Easier cooperation with other members of your site building team. Model-View-Controller
pattern used by ZF2 allows to separate business logics and presentation layer of your web
site, making its structure consistent and maintainable.
Scale your web site with the concept of modules. ZF2 uses the term module, allowing to
separate decoupled site parts, thus allowing to reuse models, views, controllers and assets
of your web-site in other works.
Accessing database in an object-oriented way. Instead of directly interacting with the
database using SQL queries, you can use Doctrine Object-Relational Mapping (ORM) to
manage the structure and relationships between your data. With Doctrine you map your
database table to a PHP class (also called an entity class) and a row from that table is
mapped to an instance of that class. Doctrine allows to abstract of database type and make
code easier to understand.

Introduction to Zend Framework 2

Write secure web sites with ZF2-provided components like form input filters and validators, HTML output escapers and cryptography algorithms, human check (Captcha) and
Cross-Site Request Forgery (CSRF) form elements.

Figure 1.1. Zend Framework sits on top of PHP and contains reusable components for building your web site

1.2 License
Zend Framework 2 is licensed under BSD-like license, allowing you to use it in both commercial
and free applications. You can even modify the library code and release it under another name.
The only thing you cannot do is to remove the copyright notice from the code. If you use Zend
Framework 2, it is also recommended that you mention about it in your sites documentation or
on About page.
Below, the Zend Framework 2 license text is presented. As you can see, it is rather short.

Introduction to Zend Framework 2

Copyright (c) 2005-2013, Zend Technologies USA, Inc.


All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Zend Technologies USA, Inc. nor the names of
its contributors may be used to endorse or promote products
derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

1.3 What Companies Prefer Zend Framework 2?


Today, there are many companies who prefer Zend Framework 2 for building their powerful web
sites. Some of them are listed on the main page of the framework.zend.com. Among them:
BBC The British Broadcasting Corporation (BBC) is a British public service broadcasting
statutory corporation .
http://framework.zend.com/
BBC From Wikipedia, the free encyclopedia http://en.wikipedia.org/wiki/BBC

Introduction to Zend Framework 2

Figure 1.2. BBC web site is based on Zend Framework 2

BNP Paribas Banque BNP Paribas is a French bank and financial services company,
European leader in global banking and financial services and is one of the six strongest
banks in the world according to the agency Standard & Poors .
BNP Paribas From Wikipedia, the free encyclopedia http://en.wikipedia.org/wiki/BNP_Paribas

Introduction to Zend Framework 2

Figure 1.3. BNP Paribas web site is based on Zend Framework 2

1.4 Release Schedule


ZF developers are known to release new versions rather frequently. This allows for security bugs
to be fixed quickly, thus allowing your sites to remain stable and secure. Release history for
recent versions of ZF2 is shown in the table 1.1:
Table 1.1. ZF2 Release History

Date

Version

March 12, 2014


October 31, 2013
August 26, 2013
August 21, 2013
June 24, 2013
June 12, 2013
May 15, 2013
May 10, 2013
May 6, 2013
May 1, 2013
April 17, 2013
April 17, 2013

ZF 2.3.0
ZF 2.2.5
ZF 2.2.4
ZF 2.2.3
ZF 2.2.2
ZF 2.2.1
ZF 2.2.0 Stable
ZF 2.2.0rc3
ZF 2.2.0rc2
ZF 2.2.0rc1
ZF 2.1.5
ZF 2.1.5

http://framework.zend.com/blog

Introduction to Zend Framework 2

Table 1.1. ZF2 Release History

Date

Version

March 14, 2013


February 21, 2013
February 20, 2013
February 06, 2013
January 30, 2012
January 30, 2012
December 19, 2012
November 25, 2012
November 20, 2012
October 17, 2012
September 21, 2012
September 20, 2012
September 05, 2013

ZF 2.1.4
ZF 2.1.3
ZF 2.1.2
ZF 2.1.1
ZF 2.1.0 Stable
ZF 2.0.7
ZF 2.0.6
ZF 2.0.5
ZF 2.0.4
ZF 2.0.3
ZF 2.0.2
ZF 2.0.1
ZF 2.0.0 Stable

As you can see from the table above, Zend Framework 2 is being constantly developed and
updated. Its developers listen to user communitys feedback and strive to keep the framework
well polished and ready for use in production systems. For the detailed list of changes between
the versions of ZF2, you can refer to the Changelog page.

1.5 Distributions
You can download the source code of Zend Framework 2 from the official site (presented in
figure 1.4) to become familiar with its structure and components.
ZF2 can be downloaded in two types: full and minimum. A full-size archive contains a complete
set of components plus demos; its size is about 3 Mb. Mimimum-size distribution contains library
components only, and its size is 3 Mb (also !).
In most cases you wont need to download the code of Zend Framework 2 manually.
Instead, you will install it with Composer dependency manager. We will become
familiar with Composer later in Chapter 2.

1.6 User Support


Support is an important thing to consider when deciding whether to use the framework as
the base for your web site or not. Support includes well written documentation, webinars,
community forums and (optionally) commercial support services, like trainings and certification
programs.
http://framework.zend.com/changelog/
http://framework.zend.com/

Introduction to Zend Framework 2

Figure 1.4. Zend Framework official project web site

Documentation. Documentation for ZF2 is located by this address. It includes beginners tutorial,
programmers manual, and API reference (API stands for Application Programming Interface).
Community Forums. Zend Framework 2 has dedicated user groups all over the world. The list of
groups can be found on this page.
Webinars are video tutorials covering various ZF2 features. Complete list of webinars can be
http://framework.zend.com/learn/
http://framework.zend.com/participate/user-groups

Introduction to Zend Framework 2

found by this link. Among webinar topics, there are:


Zend Framework 2 Patterns. Tells about whats new in ZF2 compared to the first version
of the framework. It also shows how namespaces, class autoloading, and exceptions are
used in ZF2.
Getting started with ZF2. Teaches you the basics of developing ZF2-based applications, like
creating controllers and views, manipulating services and listening to events.
The MVC architecture of ZF2. Teaches the MVC (Model View Controller) architecture of
Zend Framework 2.
Training Classes with live instructors can be accessed by this link. Here you can learn ZF2 by
doing exercises, mini-projects and developing real code.
Certification Program. Allows you to become a Zend Certified Engineer (ZCE), thus making it
easier to improve your skills as an architect and to find a job in a competitive PHP job market.

1.7 Supported Operating Systems


As any PHP web-site, ZF2-based web application can work on a Windows server, on a Linux
server and on any other operating systems PHP can run in. For instance, for creating samples
for this book, the author used Ubuntu Linux operating system.
If you do not know yet what OS to use for your web development, it is recommended for you to
use Linux, because most server software operates on Linux servers. You can refer to Appendix
A for some instructions on configuring your development environment.

1.8 Server Requirements


Zend Framework 2 requires that your server has PHP version 5.3.3 (or later) installed. Note that
this is a rather strict requirement. Not all cheap shared hostings and not all private servers have
such a modern PHP version.
Moreover, the recommended way of installing ZF2 (and other components your app depends on)
is using Composer. This forces the need of shell access (SSH) to be able to execute Composer
command-line tool. Some shared hostings provide FTP access only, so you wont be able to install
a ZF2-based web app on such servers the usual way.
What do I do if I dont have shell access to server?
If your hosting allows you to upload files through FTP protocol, you can prepare all
files on your local machine and then upload the files to the server as an archive.
http://www.zend.com/en/resources/webinars/framework
http://www.zend.com/en/services/training/course-catalog/zend-framework-2
http://getcomposer.org/

Introduction to Zend Framework 2

ZF2 utilizes URL rewriting extension for redirecting web-users to entry script of your site
(you have to enable Apaches mod_rewrite module.) You may also need to install some PHP
extensions, like memory caching extension, to improve ZF2 performance. This can be a difficulty
when using a shared hosting and requires that you have admin rights on your server.
So, if you are planning to use ZF2 on a shared web hosting, think twice. The best server to install
ZF2 on is a server with the latest version of PHP and with shell access to be able to execute
Composer, install PHP extensions and provide an ability to schedule console PHP scripts by
cron.
If your company manages its own server infrastructure and can afford upgrading PHP version
to the latest one, you can install ZF2 on your private server.
An acceptable alternative is installing a ZF2-based web application to a cloud-based hosting
service, like Amazon Web Services. Amazon provides Linux server instances as a part of EC2
service. EC2 is rather cheap, and it provides a free usage tier letting you try it for free for one
year.
Another alternative for installing a ZF2-based application is PHP Cloud. This platform as
a service solution was introduced by Zend Technologies recently. PHP Cloud allows you to
develop and install one or several of your web sites in a cloud container. This way is analogous
to Amazon EC2 hosting service, but it automatically provides you with the hardware, OS, web
server and MySQL database, so you can just create a container and start working in a minute.

1.9 Security
Zend Framework 2 follows the best practices to provide you with a secure code base for your
web sites. ZF2 creators release security bug fixes on a regular basis. You can incorporate those
fixes with a single command through Composer dependency manager.
ZF2 provides many tools allowing to make your web site secure:
Routing allows to define strict rules on how an acceptable page URL should look like. If
a site user enters an invalid URL in a web browsers navigation bar, he is automatically
redirected to an error page.
Access control lists and Role-Based Access Control (RBAC) allow to define flexible rules
for granting or denying access to certain resources of your web site. For example, an
anonymous user would have access to your index page only, authenticated users would
have access to their profile page, and the administrator user would have access to site
management panel.
Form validators and filters ensure that no unwanted data is collected through web forms.
Filters, for example, allow to trim strings or strip HTML tags. Validators are used to
check that the data that had been submitted through a form conforms to certain rules.
For example, E-mail validator checks that an E-mail field contains valid E-mail address,
and if not, raises an error forcing the site user to correct the input error.
http://aws.amazon.com/
http://aws.amazon.com/free/
http://www.phpcloud.com/

Introduction to Zend Framework 2

10

Captcha and CSRF (Cross-Site Request Forgery) form elements are used for human checks
and hacker attack prevention, respectively.
Escaper component allows to strip unwanted HTML tags from data outputted to site pages.
Cryptography support allows you to store your sensitive data (e.g. credentials) encrypted.

1.10 Performance
ZF2 creators have claimed to do a great job to improve performance of the ZF2 comparing to the
first version of the framework.
Lazy class autoloading. Classes are loaded once needed. You dont have to write require_once
for each class you want to load. Instead, the framework automatically discovers your classes
using the autoloader feature. Autoloader uses either class map or class naming conventions to
find and load the needed class.
Efficient plugin loading. In ZF2, plugin classes are instantiated only when they really need to.
This is achieved through service manager (the central repository for services of your application).
Support of caching. PHP has several caching extensions (like APC or Memcache) that can be used
to speed-up ZF2-based web sites. Caching saves frequently used data to memory to speed-up data
retrieval. For example, a Zend Framework 2 application consists of many files which require time
for PHP interpreter to process the files each time you open the page in the browser. You can use
APC extension to cache precompiled PHP opcodes to speed up your site. Additionally, you can
use the ZF2s event manager to listen to dispatching events, and cache HTML response data with
Memcache extension.
Are there any benchmark tests of ZF2 performance?
As per the authors knowledge, currently, there are no reliable benchmark tests that
would allow to compare ZF2 performance with performance of other frameworks.

1.11 Design Patterns


Zend Framework 2 creators are big fans of various design patterns. Although you dont have to
understand patterns to read this book, this section is intended to give you an idea of what design
patterns ZF2 is based on.
Model-View-Controller (MVC) pattern. Model-View-Controller pattern is used in all modern PHP frameworks. In an MVC-application you separate your code into three categories:
models (your business logics go here), views (your presentation goes here) and controllers
(code responsible for interaction with user goes here). This is also called the separation
of concerns. With MVC, you can reuse your components in a different project. It is also
easy to substitute any part of this triad. For example, you can easily replace a view with
another one, without changing your business logics.

Introduction to Zend Framework 2

11

Domain Driven Design (DDD) pattern In Zend Framework 2, by convention, you will have
model layer further splitted into entities (classes mapped on database tables), repositories
(classes used to retrieve entities), value objects (model classes not having identity), services
(classes responsible for business logics).
Additionally, you will have forms (classes responsible for collecting user input), view
helpers (reusable plugin classes intended for rendering stuff) and others.
Aspect Oriented Design pattern. In ZF2, everything is event-driven. When a site user
requests a page, an event is generated (triggered). A listener (or observer) can catch event
and do something with it. For example, a router service parses the URL and determines
what controller class to call. When the event finally reaches the page renderer, an HTTP
response is generated and the user sees the web page.
Singleton pattern. In ZF2, there is the service manager object which is the centralized
registry of all services available in the application. Each service exists in a single instance
only.
Strategy pattern. While browsing ZF2 documentation or source code, youll encounter the
word strategy for sure. A strategy is just a class encapsulating some algorithm. And you
can use different algorithms based on some condition. For example, the page renderer has
several rendering strategies, making it possible to render web pages differently based on
Accept HTTP header (the renderer can generate an HTML page, a JSON response, an RSS
feed etc.)
Adapter pattern. Adapters allow to adapt a generic class to concrete use case. For example,
Zend\Db component provides access to database in a generic way. Internally, it uses
adapters for each supported database (SQLite, MySQL, PostgreSQL and so on.)
Factory pattern. You can create an instance of a class using the new operator. Or you can
create it with a factory. A factory is just a class encapsulating creation of other objects.
Factories are useful, because they simplify dependency injection - you can provide a
generic factory interface instead of hard-coding the concrete class name. This simplifies
the testing of your model and controller classes.

1.12 Components
ZF2 developers believe that the framework should be a set of decoupled components with
minimum dependencies on each other. This is how ZF2 is organized.
The idea was to let you use some selected ZF2 components alone, even if you write your site with
another framework. This becomes even easier, keeping in mind that each component of ZF2 is
a Composer-installable package, so you can easily install any ZF2-component together with its
dependencies through a single command.
The table 1.2 lists ZF2 components with their brief description. As you can see from the table,
we can divide all ZF2 components into the following groups :
Core Components. These components are used (either explicitly or implicitly) in almost any
web application. They provide the base functionality for class auto-loading, for triggering
These component groups are not an official classification, but the authors personal point of view.

Introduction to Zend Framework 2

12

events and listening to them, for loading modules, for organizing the code within a module
in conformance to the Model-View-Controller pattern, for creating console commands and
more.
Web Forms. Forms are the way of collecting user-entered data on web pages. A form
usually consists of elements (input fields, labels, etc). For checking and filtering the userentered data, filters and validators are utilized.
User Management. This important group includes components for providing user authentication, authorization and access control. Internally, these are based on the PHP feature
called sessions.
Presentation Utilities. In this group, we can put components implementing useful web page
elements: navigation bars, progress bars, etc.
Persistence. This group contains components whose purpose is to convert in-memory data
into formats storable on a disk media (XML, JSON, a database, etc.), and vice-versa.
Testing and Debugging. In this (small) group, there are several components for logging,
testing and debugging your web site.
Web Services. This group contains components that implement various protocols for
accessing your web site programmatically (e.g. RSS, XML RPC, SOAP and so on).
Mail. Useful components for composing E-mail messages and sending them with different
transports.
Miscellaneous. Various components that cannot be put in any other group.

Table 1.2. Zend Framework 2 Components

Component Name

Description

Core Components
Zend\Cache

Provides a generic way to cache any data. Caching is used to save


frequently used data to memory (or another storage media) to
speed-up data retrieval.

Zend\Code

Provides facilities to generate arbitrary code using an object


oriented interface. Also includes annotation parsing.

Zend\Console

Provides an ability to create applications runnable from shell


command line. Console can be used, for example, for executing
scheduled actions, like mail delivery.

Zend\Di

Dependency injection. Can be used to easily substitute and replace


dependent classes.

Zend\EventManager

Allows to send events and register listeners to react to them. This


component is covered in Chapter 3.

Zend\Http

Provides an easy interface for performing Hyper-Text Transfer


Protocol (HTTP) requests. This component is covered in Chapter 4.

Zend\Loader

PHP class discovery and autoloading support. Autoloading is a more


efficient replacement for require_once. This component is covered in
Chapter 3.

Zend\ModuleManager

Zend Framework 2 module manager. In ZF2, every application consists

Introduction to Zend Framework 2

Table 1.2. Zend Framework 2 Components

Component Name

Description
of modules. This component is covered in Chapter 3.

Zend\Mvc

Support of Model-View-Controller pattern. Separation of business


logic from presentation. This component is covered in Chapter 4.

Zend\ServiceManager

Service manager. This is the registry of all services available


in the application, making it possible to access services from
any point of the web site. This component is covered in Chapter 3.

Zend\Stdlib

Miscellaneous utility classes: string utils, array utils,


serializable queues, etc.

Zend\View

Provides a system of helpers, output filters, and variable


escaping. Used in presentation layer. This component is covered in
Chapter 4.

Zend\Uri

A component that aids in manipulating and validating Uniform


Resource Identifiers (URIs).

Persistence
Zend\Dom

Provides tools for working with DOM documents and structures.


This includes querying DOM trees with CSS selectors and XPath.

Zend\Db

Provides database access in cross-database style.

Zend\Json

Provides convenience methods for serializing native PHP to JSON and


decoding JSON to native PHP. Used for object serialization.

Zend\Serializer

Provides an adapter based interface to simply generate storable


representation of PHP types by different facilities, and recover them.

User Management
Zend\Authentication

Provides an API for user authentication. Users are typically


authenticated by providing a username and password which are
compared against a database table or Apache password file.

Zend\Permissions

Access control lists (ACLs) and role-based access control (RBAC).

Zend\Session

Manage and preserve session data, a logical complement of cookie


data, across multiple page requests by the same client.

Presentation Utilities
Zend\Barcode

Provides a generic way to generate barcodes. A barcode is a


small bar containing stripes of various width and is optically
readable by a machine. You may have seen barcodes when purchasing
goods in a supermarket. This component is covered in Chapter 5.

Zend\Captcha

Human input check. Generates a random image ensuring that your


sites user is not a robot. This component is covered in Chapter 10.

Zend\Navigation

Sitemaps, breadcrumbs and site navigation trees.

Zend\Paginator

Breaking large tabular data results into pages.

13

Introduction to Zend Framework 2

14

Table 1.2. Zend Framework 2 Components

Component Name

Description

Zend\ProgressBar

Component to create and update progress bars in different environments.

Zend\Escaper

Smart class for escaping text output. Used to secure web site views.

Zend\Tag

A component suite which provides a facility to work with taggable items.

Testing and Debugging


Zend\Debug

A small component containing a debugging utility class.

Zend\Log

Component for general purpose logging. Logging site operations is used to


troubleshoot possible errors with your site in development and production
environments.

Zend\Test

Base classes for unit testing and test bootstrapping.

Web Forms
Zend\Filter

Provides a set of commonly needed data filters, like string trimmer. This
component is covered in Chapter 8.

Zend\Form

Web form data collection, filtering, validation and rendering. This


component is covered in Chapter 7 and Chapter 10.

Zend\InputFilter

Provides an ability to define form data validation rules. This component is


covered in Chapter 7.

Zend\Validator

Provides a set of commonly needed validators. This component is covered


in Chapter 9.

Web Services
Zend\Feed

Provides functionality for consuming RSS and Atom feeds.

Zend\Ldap

Provides support for Lightweight Directory Access Protocol (LDAP)


operations including but not limited to binding, searching, and
modifying entries in an LDAP directory.

Zend\Server

Client-server generic class interfaces.

Zend\Soap

Implementation of Simple Object Transfer Protocol (SOAP).

Zend\XmlRpc

Used for creation of web-services utilizing XML Remote Procedure


Call (RPC) protocol.

Mail
Zend\Mail

Provides generalized functionality to compose and send both text


and MIME-compliant multi-part E-mail messages. This component is
covered in Chapter 7.

Zend\Mime

Support class for Multipurpose Internet Mail Extensions (MIME)


messages.

Miscellaneous
Zend\Config

Provides a nested object property based user interface for


accessing the configuration data within application code.

15

Introduction to Zend Framework 2

Table 1.2. Zend Framework 2 Components

Component Name

Description

Zend\Crypt

Contains implementation of symmetric and asymmetric cryptographic


algorithms.

Zend\File

PHP class file discovery.

Zend\I18n

Support of multi-lingual web sites.

Zend\Math

Big integer support and some auxiliary math functionality.

Zend\Memory

This class encapsulates memory management operations, when PHP


works in limited memory mode.

Zend\Text

Various text utilities like character tables and FIGlets.

Zend\Version

Allows to retrieve the version of Zend Framework. This component is


covered in Chapter 4.

1.13 ZF2 Service Components


In addition to standard Zend Framework 2 components described in the previous section,
there are so called Services for Zend Framework 2 components. Those components provide
implementations of API for accessing various popular web resources (e.g. Flickr, Twitter,
SlideShare, reCaptcha and so on) programmatically. Table 1.3 contains the list of (currently
available) service components together with their brief descriptions:
Component Name

Description

ZendService\Akismet

Provides API for accessing Akismet (a spam


filtering service for your blog).

ZendService\Amazon

Provides API for using Amazon web services.


Amazon provides a number of web services, among them EC2 (web
hosting in the cloud), S3 (storage in the cloud) and others.

ZendService\AppleApns

Provides a client for the Apple Push Notification Service (APNs for
short), which is a service for propagating information to iOS and
Mac OS X devices.

ZendService\Audioscrobbler

API for using the Audioscrobbler service, which is a database that


tracks listening habits.

ZendService\Delicious

API for using del.icio.us services. Provides access to posts at


del.icio.us and read-only access to public data of all users.

ZendService\DeveloperGarden

Provides API for accessing services of Deutsche Telekom, such as


voice connections or sending SMS messages.

http://akismet.com/
http://aws.amazon.com/
http://www.audioscrobbler.net/
https://delicious.com/

16

Introduction to Zend Framework 2

Component Name

Description

ZendService\Flickr

API for using the Flickr photo sharing service.

ZendService\Google\Gcm

Provides a client for the Google Cloud Messaging API.

ZendService\LiveDocx

Provides API to LiveDocx service that allows to generate PDF,


DOCX, DOC, HTML or RTF files.

ZendService\Nirvanix

API for using Nirvanix service which provides an Internet Media


File System (IMFS), a storage service that allows applications to
upload, store and access files.

ZendService\Rackspace

API to manage the Rackspace services Cloud Files and Cloud


Servers.
Provides API for the reCAPTCHA service, used to digitize books
and (as a side product) generate CAPTCHA images.

ZendService\ReCaptcha
ZendService\SlideShare

Access to the SlideShare services for hosting slide shows online.

ZendService\StrikeIron

API for accessing the StrikeIron web services Cloud-Based Data


Quality & Enhancement Solutions.

ZendService\Technorati

Provides interface for using Technorati, which is a place storing


individual reviews, essays, interviews, and news stories.

ZendService\Twitter

Provides API to Twitter microblogging service.

ZendService\Windows Azure

Provides API for accessing Microsoft Windows Asure cloud web


hosting platform.

Because the API to above mentioned web resources may be changed by their vendors
without a notice, those components are not part of the core Zend Framework 2
distribution. By the same reason, those components are not discussed deeply in this
book.

1.14 Differences with Zend Framework 1


For readers who have an experience in Zend Framework 1, in this section well give some
information on what has changed in Zend Framework 2.
Below, the main technical differences between ZF1 and ZF2 are presented:

1.14.1 Backwards Compatibility


ZF1s architecture passed an evolutionary path, preserving backwards compatibility and accumulating many solutions which were not as efficient as they could be. ZF2 has been rewritten
http://www.flickr.com/
http://www.google.com/recaptcha
http://www.slideshare.net/
http://www.strikeiron.com/
http://technorati.com/
http://twitter.com
http://www.windowsazure.com/

Introduction to Zend Framework 2

17

from scratch to implement the best features of ZF1 in a better, more efficient and scalable way.
Because of these breaking changes, ZF2 is not backwards-compatible with ZF1.

1.14.2 ZFTool
In Zend Framework 1, you used ZFTool for creating the application, adding layouts and
controllers. In ZF2, you create your new applications by downloading Zend Skeleton Application
available on GitHub. By the way, in ZF2 you can install a component called ZFTool, and it can
also create the skeleton application or a module for you.

1.14.3 Modules
In Zend Framework 1, your application was monolithic (although there was a concept of module).
In ZF2, everything is a module. The skeleton application has single Application module by
default. Each module may contain configuration, models, views, controllers and the assets (e.g.
database tables, files etc.) A module can call classes from other modules. You can install thirdparty modules and reuse your own modules across applications.

1.14.4 Aspect Oriented Design


In ZF2, events are used to make it possible to decouple modules. You can install a module, and it
will just work by listening to events occurring in the application without knowing about other
modules. Events include bootstrapping, routing, dispatching and rendering.

1.14.5 Namespaces

In ZF1, you worked with long underscore-separated class names like Zend_Controller_Action.
In ZF2, PHP namespaces are used, so instead youll have something like Zend\MVC\Controller\AbstractActionC
which can be easier to type with auto-completion feature and easier to understand. Namespaces
allow to define short class names (aliases) and use them instead of full names. By convention,
namespaces are mapped to directory structure, making it easier to perform class autoloading.

1.14.6 Configuration
In ZF1, you had an application-level INI config file. In ZF2, each module has its configuration
file in a form of PHP array. At application level, module configurations are finally merged into
a single large nested PHP array.

1.14.7 Service Manager


In ZF1, you had an application registry of classes, which allowed you to access application
services and even put your own class to the registry and use it later. In ZF2, we have the
service manager, which is a more complex version of the registry, implementing lazy loading
and dependency injection. With service manager, you can register a service class and use it
across your modules. For example, Doctrine ORM library registers the Entity Manager service
which you can use to access the database across the module controllers.

Introduction to Zend Framework 2

18

1.15 Competing Frameworks


Zend Framework is not the only web development framework. There are others, like Symfony,
Cake PHP, CodeIgniter and Yii Framework. To estimate the average popularity of these
frameworks in some way, we can use Google Trends site, which allows to track count of a
keyword search queries over time. For example, if you enter Zend Framework, CakePHP, Yii,
CodeIgniter, Symfony into the query field, you will get the graph as shown in figure 1.5.
As you can see from the graph, Zend Framework (blue line) has reached its popularity peak
by 2010, and since then it has slowly lost its popularity. However, ZF is still one of the strong
players on the market. On the other hand, Cake PHP, Symfony, CodeIgniter and Yii framework
are becoming highly popular nowadays.
Lets also look at the relative popularity of ZF1 and ZF2 by typing Zend Framework, Zend
Framework 2 into the search query field. The result is shown in figure 1.6.

Figure 1.5. Popularity of PHP frameworks. Powered by Google Trends

As we can see, Zend Framework 2 (the red line) was released not so long ago, and has yet to
become popular. The author believes that ZF2 has all the necessary qualities to become popular
over time.
http://symfony.com/
http://cakephp.org/
http://ellislab.com/codeigniter
http://www.yiiframework.com/
http://www.google.ru/trends/

19

Introduction to Zend Framework 2

Figure 1.6. Popularity of Zend Framework 2 comparing to the first version. Powered by Google Trends

If you are familiar with one of the above mentioned frameworks, in table 1.4 you can find the
comparison of features provided by those PHP frameworks. Capabilities of Zend Framework 2
are marked with bold.
Table 1.4. Comparison of Features provided by PHP frameworks

Feature

ZF2

Symfony 2

Cake PHP

CodeIgniter

Yii

Current
version
Distribution
archive size

2.2.1

2.3.1

2.3.6

2.1.3

1.1.13

3 Mb

4.4 Mb

2.0 Mb

2.2 Mb

3.9 Mb

Installation

Composer

Composer

Archive

Archive

Archive

Compatibility
with
shared
hostings

Bad (requires
SSH and
vhosts)

Bad (requires
SSH and
vhosts)

Good

Good

Good

Monolithic or
componentbased?

Components

Components

Components

Components

Monolithic

Prefer
conventions
or configs?

Configuration Configuration

Conventions

Conventions

Conventions

Database
access
pattern

Data Mapper

Active Record

Traditional

Active Record,

or
Active Record

PDO

Data Mapper

(Doctrine/ORM),(Doctrine/ORM)
Table
Gateway,
Row
Gateway

20

Introduction to Zend Framework 2

Table 1.4. Comparison of Features provided by PHP frameworks

Feature

ZF2

Symfony 2

Cake PHP

CodeIgniter

Yii

Database

Yes
(Doctrineprovided)

Yes (Doctrine-

Yes

Yes

Yes

Twitter

Twitter

Any

Any

Blueprint CSS

Bootstrap

Bootstrap

View
Template
Language

Any you
want, or
none at all

Twig

Smarty/Twig

Any you
want,
or none

None or Prado

Unit testing

Yes
(PHPUnit)

Yes (PHPUnit)

Yes (PHPUnit)

Yes (PHPUnit)

Yes (PHPUnit-

Yes
(PHPUnit)

Yes (PHPUnit)

Yes

No

Yes (Selenium)

Yes
(Doctrineprovided)

Yes (Doctrine

Yes

No

Yes

migrations
CSS
Framework

support
Functional
testing
Database
fixtures

provided)

(recommended)

based)

bundle)

Summarizing the table above, we can say that:


Zend Framework 2 can be considered as one of the most mature and established PHP
frameworks on the market. This allows to be sure that ZF2 creators wont stop to update
and support it unexpectedly.
The major way for installing ZF2 is through Composer dependency manager. Symfony 2
is similar to ZF2 in this sence. Other PHP frameworks utilize the conventional installation
from an archive.
ZF2 (as Symfony 2) has bad compatibility with shared hostings because of the Composerbased installation method and strict PHP version requirements. So, if you need to install
your website to a shared hosting, you probably need to contact the hostings support for
installation instructions.
ZF2 provides the sophisticated configuration methods, so you can fine-tune and override
any aspect of its work. Some other PHP frameworks prefer the conventions over
configuration way, making it easier for newbies to start developing websites.
For the presentation layer, ZF2 suggests the use of Twitter Bootstrap CSS Framework by
default. But this does not limit you on using any other CSS frameworks.
In ZF2, you can use several database access methods. And like in most PHP frameworks,
you can benefit from using an object-oriented way of managing the database (with
Doctrine ORM). Additionally, you can use Doctrine migrations mechanism for managing
the database schema in a standardized way.
Comparing to other frameworks, ZF2 provides good capabilities for unit- and functional
testing (based on PHPUnit framework). This makes it possible to automate the testing of

Introduction to Zend Framework 2

21

the code you write. For testing the database functionality, you can use Doctrine-provided
fixture mechanism.

1.16 Summary
A PHP framework is a library, giving you the code base and defining consistent ways of creating
web applications. Zend Framework 2 is a modern web development framework created by
the Zend Company, the vendor of PHP language. It provides the developers with outstanding
capabilities for building scalable and secure web sites. ZF2 is licensed under BSD-like license and
can be used for free in both commercial and open-source applications.
ZF2 is updated frequently, making your sites more resistant to vulnerabilities and security holes.
On its official site, ZF2 provides the documentation (tutorials and API reference), webinars,
community forums and commercial support services, like trainings and certification program.
ZF2 creators strive to improve the performance of ZF2 in comparison to the first version of the
framework. Among the features that contribute into the performance of ZF2, there are lazy class
autoloading and support of caching.
On the market, Zend Framework is not the only web development framework. ZF2 is positioned
as a good framework for corporate applications because of its pattern-based design and
scalability. However, you can use ZF2 in any-sized web application with great success.
Ive found a mistake in this chapter/have a suggestion. What do I do?
Please contact the author using the dedicated Forum. Alternatively, you can contact
the author through his E-mail address (olegkrivtsov@gmail.com). The author appreciates your feedback and will be happy to answer you and improve this book.

https://leanpub.com/using-zend-framework-2/feedback

2 Zend Skeleton Application


Zend Framework 2 provides you with the so called skeleton application to make it easier to
create your new applications from scratch. In this chapter, we will download and install the
skeleton application which can be used as a base for creating your web sites. It is recommended
that you refer to Appendix A before reading this chapter to get your development environment
configured.

2.1 Getting Zend Skeleton Application


The Skeleton Application is a simple ZF2-based application that contains most necessary things
for creating your own simple web site. The skeleton applications code is stored on GitHub code
hosting and can be publicly accessed by this link. To download the source code of the skeleton
application as a ZIP archive, click the Download ZIP button (see the figure 2.1 below).
To download the code archive on a Linux machine without graphical interface, you
can use the wget command, like this:
wget https://github.com/zendframework/ZendSkeletonApplication/archive/master.zip

Unpack the downloaded ZIP archive to some directory. If you are programming in Linux, it is
recommended to unpack it in your home directory:
cp /path/to/downloaded/archive/ ZendSkeletonApplication-master.zip ~
cd ~
unzip ZendSkeletonApplication-master.zip

The commands above will copy the file ZendSkeletonApplication-master.zip archive that
youve downloaded to your home directory, then unpack the archive.
If you are using Windows, you can place the skeleton app directory anywhere in your
system, but ensure that file and directory access rights are sufficient for your web server
to read and write the directory and its files. Actually, if you dont put your files to
C:\Program Files, everything should be OK.
https://github.com/zendframework/ZendSkeletonApplication

Zend Skeleton Application

23

Figure 2.1. Zend Skeleton Applications code is stored on GitHub

2.2 Typical Directory Structure


Every ZF2-based web-site (including the skeleton app) is organized in the same recommended
way. Of course, you can configure your application to use a different directory layout, but this
may make it difficult to support your web-site by other people who are not familiar with such a
directory structure.
Lets have a brief look at the typical directory structure (see figure 2.2):
As you can see, in the top-level directory (we will denote it APP_DIR from now on), there are
several files:
README.md is a text file containing a brief description of the skeleton application;
LICENSE.txt is a text file containing ZF2 license (you had a chance to read it in Chapter 1
of this book);
composer.phar is an executable PHP archive containing the code of Composer dependency
management tool; we will use it later;
composer.json is a JSON configuration file for Composer.

24

Zend Skeleton Application

Figure 2.2. Typical Directory Structure

And we also have several subdirectories:


The config directory contains application-level configuration files.
The data directory contains the data your application might create; it may also contain cache
used to speed-up Zend Framework.
The module directory contains all application modules. Currently there is a single module called
Application. The Application is the main module of your web-site. You can also put other
modules here, if you wish. We will talk about the modules a little bit later.
The vendor directorys purpose is to contain third-party library files, including Zend Framework
2 library files. Currently this directory is almost empty, but we will install all required libraries
later.
The public directory contains data publicly accessible by the web-user. As you can see, webusers will mainly communicate with the index.php, which is also called the entry point of your
web site.
Your web site will have a single entry point, index.php, because this is more secure than
allowing anyone to access all your PHP files.

Inside of the public directory, you can also find .htaccess file. Its main purpose is to define URL
rewriting rules, but you also can use it to define access rules for your web-site. For example, with
.htaccess you can grant access to your web-site from your own IP address only, or use HTTP
authorization to request users for username and password.
The public directory contains several subdirectories also publicly accessible by web-users:
css subdirectory contains all publicly accessible CSS files;
img subdirectory contains publicly accessible images (*.JPG, *.PNG, *.GIF, *.ICO, etc.);

Zend Skeleton Application

25

and js subdirectory stores publicly accessible JavaScript files used by your web-pages.
Typically, files of jQuery library are placed here, but you can put your own JavaScript
files here, too.

What is jQuery library?


jQuery is a JavaScript library which was created to simplify the client-side scripting
of HTML pages. jQuerys selector mechanism allows to easily attach event handlers to
certain HTML elements, making it really simple to make your HTML pages interactive.

Because the Zend Skeleton Application is stored on GitHub, inside of the directory structure,
you can find hidden .gitignore and .gitmodules files. These are GIT version control systems
files. You can ignore them (or even remove them if you do not plan to store your code in a GIT
repository).
Because we will later use the skeleton as the base for our Hello World application, lets rename
the ZendSkeletonApplication-master directory into helloworld, which also sounds shorter. In
Linux, you can do that with the following command:
mv ZendSkeletonApplication-master helloworld

2.3 Installing Dependencies with Composer


When writing a ZF2-based web-site, you are recommended to use Composer for installation
of your applications dependencies. A dependence is some third-party code your app uses. For
example Zend Framework 2 is the dependence for your web-site.
In Composer, any library is called a package. All packages installable by Composer are registered
on Packagist.org site. With Composer, you can identify the packages that your app requires and
have Composer to download and install them automatically.
The dependencies of the skeleton application are declared in APP_DIR/composer.json file (see
below):

http://jquery.com/
http://git-scm.com/
http://getcomposer.org/
https://packagist.org/

Zend Skeleton Application

26

Contents of composer.json file

{
"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.2.0rc1"
}
}

What is JSON?
JSON (JavaScript Object Notation), is a text-based file format used for human-readable
representation of simple structures and nested associative arrays. Although JSON
originates from Java, it is used in PHP and in other languages, because it is convenient
for storing configuration data.

In that file, we see some basic info on the skeleton application (its name, description, license,
keywords and home page). You will typically change this info for your future web-sites. This
information is optional, so you can even safely remove it, if you do not plan to publish your web
application on Packagist catalog.
What is interesting for us now is the require key. The require key contains the dependencies
declarations for our application. We see that we require PHP engine version 5.3.3 or later and
Zend Framework 2.2.0 Release Candidate 1 or later.
The information contained in composer.json file is enough to locate the dependencies, download
and install them into the vendor subdirectory. Lets finally do that by typing the following
commands from your command shell (replace APP_DIR placeholder with your actual directory
name):
cd APP_DIR
php composer.phar self-update
php composer.phar install

The commands above will change your current working directory to APP_DIR, then self-update
the Composer to the latest available version, and then install your dependencies. By the way,
Composer does not install PHP for you, it just ensures PHP has an appropriate version, and if
not, it will warn you.

27

Zend Skeleton Application

If you look inside the vendor subdirectory, you can see that it now contains a lot of files. Zend
Framework 2 files can be found inside the APP_DIR/vendor/zendframework/zendframework/library
directory (figure 2.3). Here you can encounter all the components that we described in Chapter
1 (Authentication, Barcode, etc.)

Figure 2.3. Vendor directory

In some other frameworks, another (conventional) way of dependency installation is


used. You just download the dependency library as an archive, unpack it and put it
somewhere inside of your directory structure (typically, to vendor directory). This
approach was used in Zend Framework 1.

2.4 Apache Virtual Host


Now we are almost ready to get our skeleton web-site live! The last thing we are going to do is
configure an Apache virtual host. A virtual host term means that you can run several web-sites on
the same machine. The virtual sites are differentiated by domain name (like site.mydomain.com
and site2.mydomain.com) or by port number (like localhost and localhost:8080). Virtual hosts
work transparently for site users, that means users have no idea whether the sites are working
on the same machine or on different ones.

Zend Skeleton Application

28

Can I install the web-site to /var/www directory without virtual hosts?


With ZF2-based web sites, it would be more convenient to use Apache virtual hosts
instead of putting the files inside of /var/www. This is because the public subdirectory
needs to be the document root of your site. If you put an entire application in /var/www,
which is the document root by default, you would have to additionally configure the
.htaccess file to forbid access to everything except the public subdirectory. With
virtual host configuration this is a bit easier to do.

Currently, we have the skeleton application inside of home folder. To let Apache know about it,
we need to edit the virtual host file.
Virtual host file may be located at a different path, depending on your
operating system type. For example, in Linux Ubuntu it is located in
/etc/apache2/sites-available/000-default file. Moreover, virtual host file
name and content may look differently depending on Apache HTTP Servers version.
For OS- and server-specific information about virtual hosts, please refer to Appendix
A.

Lets now edit the default virtual host file to make it look like below (this example is applicable
to Apache v.2.2):
Virtual host file

<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /home/username/helloworld/public
<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
<Directory /home/username/helloworld/public/>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
allow from all
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
</VirtualHost>

Zend Skeleton Application

29

Line 1 of the file makes Apache to listen to all (*) IP addresses on port 80.
Line 2 defines the web masters E-mail address. If something bad happens to the site, Apache
sends an alert E-mail to this address. You can enter your E-mail here.
Line 4 defines the document root directory (APP_DIR/public). All files and directories under
the document root will be accessible by web-users. You should set this to be the absolute
path to skeleton applications public directory. So, the directories and files inside public (like
index.php, css, js, etc.) will be accessible, while directories and files above public directory
(like config, module, etc.) wont be accessible by web users, which enhances the security of the
web site.
Lines 5-8 define default access rules for directories. These rules are rather strict. The Options
FollowSymLinks directive allows Apache to follow symbolic links (in Linux, a symbolic links is
an analog of a shortcut in Windows). The AllowOverride None directive forbids overriding any
rules using .htaccess files.
Lines 9-14 define rules for the document root directory (APP_DIR/public). These rules override
the default rules mentioned above. For example, the AllowOverride All directive allows to
define any rules in .htaccess files. The Order allow,deny and allow from all control a threepass access control system, effectively allowing everyone to visit the site.
Line 16 defines the path to error.log file, which can be used to troubleshoot possible errors
occurring in your site code. Line 23 defines the logging level to use (the warn means that warnings
and errors will be written to log).
Lines 18-19 are comments and ignored by Apache. You mark comments with the hash (#)
character.
Zend Framework 2 utilizes Apaches URL rewriting module for redirecting web-users
to entry script of your web-site. Please ensure that your web-server has mod_rewrite
module enabled. For instructions on how to enable the module, please refer to Appendix
A.

After editing the config file, do not forget to restart Apache to apply your changes.

2.5 Opening the Web Site in Your Browser


To open the web site, type http://localhost in your browsers navigation bar and press Enter.
Figure 2.3 shows the site in action.
On the page that appears, you can see the navigation menu at the top. The navigation bar
currently contains the single link named Home. Under the navigation bar, you can see the
Welcome to Zend Framework 2 caption. Below the caption, you can find some advices for
beginners on how to develop new ZF2-based applications.

30

Zend Skeleton Application

Figure 2.3. Zend Skeleton Application

2.6 Creating NetBeans Project


Now that we have the skeleton application set up and working, we will want to change something
with it in the future. To easily navigate the directory structure, edit files and debug the web site,
the common practice is to use an IDE (Integrated Development Environment). In this book, we
use NetBeans IDE (see Appendix A for more information on how to install NetBeans).
To create NetBeans project for our skeleton application, run NetBeans and open menu File->New
Project. The New Project dialog appears (see figure 2.4).
In the Choose Project page that appears, you should choose PHP project type and in the right
list select Application with Existing Source (because we already have the skeleton applications
code). Then click the Next button to go to the next page (shown in figure 2.5).
In the Name and Location dialog page, you should enter the path to the code (like /home/username/helloworld), the name for the project (for example, helloworld) and specify the version of
PHP your code uses (PHP 5.3 or later). The PHP version is needed for the NetBeans PHP syntax
checker which will scan your PHP code for errors and highlight them. Press the Next button to
go to the next dialog page (shown in figure 2.6).

Zend Skeleton Application

Figure 2.4. Creating NetBeans Project - Choose Project Page

Figure 2.5. Creating NetBeans Project - Name and Location Page

31

Zend Skeleton Application

32

Figure 2.6. Creating NetBeans Project - Choosing Configuration Page

In the Run Configuration page, it is recommended that you specify the way you run the web site
(Local Web Site) and web site URL (http://localhost). Keep the Index File field empty (because
we are using mod_rewrite, the actual path to your index.php file is hidden by Apache). If you
are seeing the warning message like Index File must be specified in order to run or debug project
in command line, just ignore it.
Click the Finish button to create the project. When the helloworld project has been successfully
created, you should see the project window (see the figure 2.7).
In the project window, you can see the menu bar, the tool bar, the Projects pane where your
project files are listed, and, in the right part of the window, you can see the code of the index.php
entry file.
Please refer to Appendix B for more NetBeans usage tips, including launching and interactively
debugging ZF2-based web sites.
Its time for some advanced stuff
Congratulations! Weve done the hard work of installing and running the Zend Skeleton
Application, and now its time to have a rest and read about some advanced things in
the last part of this chapter.

2.7 Hypertext Access File (.htaccess)


Weve mentioned the APP_DIR/public/.htaccess file when talking about typical directory
structure. Now lets try to understand the role of this file and how we can use it.

33

Zend Skeleton Application

Figure 2.7. NetBeans Project Window

The .htaccess (hypertext access) file is actually an Apache web servers configuration file
allowing to override some web servers global configuration. The .htaccess file is a directorylevel configuration, which means it affects only its owning directory and all sub-directories.
The content of .htaccess file is presented below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

RewriteEngine On
# The following rule tells Apache that if the requested filename
# exists, simply serve it.
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
# The following rewrites all other queries to index.php. The
# condition ensures that if you are using Apache aliases to do
# mass virtual hosting, the base path will be prepended to
# allow proper resolution of the index.php file; it will work
# in non-aliased environments as well, providing a safe, one-size
# fits all solution.
RewriteCond %{REQUEST_URI}::$1 ^(/.+)(.+)::\2$
RewriteRule ^(.*) - [E=BASE:%1]
RewriteRule ^(.*)$ %{ENV:BASE}index.php [NC,L]

34

Zend Skeleton Application

Line 1 tells Apache web server to enable URL rewrite engine (mod_rewrite). The rewrite engine
modifies the incoming URL requests, based on regular expression rules. This allows you to map
arbitrary URLs onto your internal URL structure in any way you like.
Lines 4 - 7 define rewrite rules that tell the web server that if the client (web browser) requests
a file that exists in the document root directory, than to return the contents of that file as HTTP
response. Because we have our public directory inside of the virtual hosts document root, we
allow site users to see all files inside of the public directory, including index.php, CSS files,
JavaScript files and image files.
Lines 14 - 17 define rewrite rules that tell Apache what to do if the site user requests a file which
does not exist in document root. In such a case, the user should be redirected to index.php.
Table 2.2 contains several URL rewrite examples. The first and second URLs point to existing
files, so mod_rewrite returns the requested file paths. The URL in the third example points to
a non-existent file htpasswd (which may be a symptom of a hacker atack), and based on our
rewrite rules, the engine returns index.php file.
Table 2.2. URL rewrite examples

Requested URL

Rewritten URL

http://localhost/index.php

File exists; return the local file


APP_DIR/public/index.php

http://localhost/css/bootstrap.css

File exists; return the local file


APP_DIR/public/css/bootstrap.css

http://localhost/htpasswd

File does not exist; return


APP_DIR/public/index.php instead.

2.8 Blocking Access to the Web Site by IP Address


Sometimes it may be required to block access to your web site from all other IP addresses except
yours. For example, when you develop a web site, you dont want someone to see your incomplete
work. Also, you may not want to let Google or other search engines to index your web site.
To forbid access to your site, you can modify the .htaccess file and add the following lines to
the end:
1
2
3

Order allow,deny
Deny from all
Allow from 127.0.0.1

Line 1 defines the order. Order allow deny means allow everything which is not denied.
Line 2 forces Apache to deny access to your site for everyone.
Line 3 overrides the line 2, allowing access to your site from IP 127.0.0.1 (localhost). You may
need to replace 127.0.0.1 with your external IP address.

Zend Skeleton Application

35

How do I determine my IP address?


You can use the http://www.whatismyip.com web site to determine your external IP
address. The external IP address is the address by which other computers on the Internet
may access your site.

2.9 HTTP Authentication


You may want to allow access to your site to certain users. For example, when you are
demonstrating your web site to your boss, you will give her username and password for logging
into your site.
To allow access to your web site by username and password, you can modify the .htaccess file
and add the following lines to the end:
1
2
3
4

AuthUserFile /usr/local/apache/passwd/passwords
AuthType Basic
AuthName "Authentication Required"
require valid-user

Line 1 defines the file where passwords will be stored. This file should be created with the
htpasswd utility.
Line 2 defines Basic authentication method. The most common method is Basic. It is important
to be aware, however, that Basic authentication sends the password from the client to the
server unencrypted. This method should therefore not be used for highly sensitive data. Apache
supports one other authentication method: AuthType Digest. This method is much more secure.
Most recent browsers support Digest authentication.
Line 3 defines the text that will be displayed to user when he tries to log in.
Line 4 will allow anyone to log in that is listed in the password file, and who correctly enters
their password.
To create passwords file, type the following command:
htpasswd -c /usr/local/apache/passwd/passwords <username>

In the command above, you should replace the <username> placeholder with the name of the
user. You can choose an arbitrary name, for example admin. The command will request the
users password and write the password to the file:

http://www.whatismyip.com/

Zend Skeleton Application

36

# htpasswd -c /usr/local/apache/passwd/passwords <username>


New password:
Re-type new password:
Adding password for user <username>

When the user tries to visit the site, he sees the HTTP authentication dialog (see the figure below).
To log into your site, the visitor should enter the correct username and password.
For additional information on HTTP authentication, you can refer to Authentication
and Authorization topic of Apache documentation.

2.10 Having Multiple Virtual Hosts


When developing several web sites on the same machine, you will want to create several virtual
hosts. For each virtual host you need to specify a domain name (like site1.mydomain.com). But
if you currently dont have a domain name, you can specify a different port instead (see the
example below).
# Listen directive tells Apache to listen requests on port 8080
Listen 8080
NameVirtualHost 8080
<VirtualHost *:8080>
...
</VirtualHost>

To access the web site, in your browsers navigation bar, enter http://localhost:8080.
After editing the virtual host config file, you should restart Apache to apply changes.

2.11 Hosts File


When you have multiple local web sites mapped to different ports, it becomes difficult to
remember which port each site presents. To simplify this, you can define an alias for your web
site.
To do this, you should edit the hosts file. The hosts file is a system file which contains mappings
between IP addresses and host names. The hosts file contains lines of text consisting of an IP
address in the first text field followed by one or more host names.
To add an alias for your local web sites, add lines for each of your web site as shown in the
example below.
http://httpd.apache.org/docs/current/howto/auth.html

37

Zend Skeleton Application

127.0.0.1
127.0.0.1:8080

site1.localhost
site2.localhost

So now youll be able to simply enter site1.localhost in your browsers address bar instead of
remembering the port number.
In Linux, the hosts file is located in /etc/hosts. In Windows, the file is typically
located in C:\Windows\System32\drivers\etc\hosts. To edit the file, you need to be
an administrator. Please also note that some anti-virus software may block changes
to hosts file, so youll have to temporarily disable your anti-virus to edit the file, and
enable it after.

2.12 Advanced Composer Usage


Earlier in this chapter, we have used Composer to install Zend Framework 2 library code. Now
lets briefly describe some advanced Composer usage examples.
As we already know, the only required key in the composer.json file is require. This key tells
what packages are required by your application:
{
"require": {
"php": ">=5.3.3",
"zendframework/zendframework": ">2.2.0rc1"
}
}

2.12.1 Package Names and Versions


A package name consists of two parts: vendor name and project name. For example zendframework/zendframework package name consists of zendframework vendor name and
zendframework project name. You can search for other packages from zendframework
vendor through Packagist.org web site (see the figure 2.8 for an example).
A package also has an associated version number. A version number consists of major number,
minor number, optional build number, and optional stability suffix (.e.g b1, rc1). Within the
require key we specify which versions of the package are acceptable. For example, >2.2.0rc1
means that we can install versions greater than 2.2.0rc1 (rc1 stands for Release Candidate
1), e.g.: 2.2.0rc2, 2.2.0, 2.2.1 and so on. In table 2.1, possible ways of specifying acceptable
versions are presented:
https://packagist.org/search/?q=zendframework

38

Zend Skeleton Application

Figure 2.8. You can search packages on Packagist.org


Table 2.1. Package Version Definitions

Definition Example

Description

2.2.0

Exact version. In this example, only the version 2.2.0 can be installed.

>=2.2.0

Greater or equal version can be installed (2.2.0, 2.2.1, etc.)

>2.2.0

Greater version can be installed (2.2.1 etc.)

<=2.2.0

Lower or equal version can be installed (1.0, 1.5, 2.0.0, 2.2.0 etc.)

<2.2.0

Lower version can be installed (1.0, 1.1, 1.9, etc.)

!=2.2.0

All versions except this version can be installed.

>=2.0,<2.2.0

Any version belonging to this range of versions can be installed.

2.*

Any version having major number equal to 2 can be installed (minor number
can be any).

2.2

Any version starting from 2.2, but lower than the next major version
(equivalent to >=2.2,<3.00).

2.12.2 Installing and Updating Packages


Weve already used the install command to install our dependencies. As soon as you call
this command, Composer will find, download and install the dependencies to your vendor
subdirectory.

Zend Skeleton Application

39

Is it safe to install dependencies with Composer?


Well, some people may be afraid of Composer-style dependency management, because
they think someone can update the dependencies system-wide by mistake or intentionally, causing the web application to break. Note, that Composer never installs these
system-wide, instead it installs them into your APP_DIR/vendor/ directory.

After installation, Composer also creates the APP_DIR/composer.lock file. This file now contains
actual versions of the packages that were installed. If you run the install command again,
Composer will encounter the composer.lock file, check which dependencies already installed
and as all packages already installed, it just exits without doing anything.
Now assume that in some period of time new security updates for your dependency packages
are released. You will want to update your packages to keep your web site secure. You can do
that by typing the following:
php composer.phar update

If you want to update only a single dependency, type its name as the following:
php composer.phar update zendframework/zendframework

After the update command, your composer.lock file will be updated, too.
What do I do if I want to roll back to a previous version of the package?
If the update procedure resulted in unwanted problems with your system, you can roll
back by reverting the changes to your composer.lock file and issuing the install
command again. Reverting changes to composer.lock is easy if you use a version
control system, like GIT or SVN. If you dont use a version control system, make a
backup copy of composer.lock before updating.

If you want to add new dependency to the application, you can either edit composer.json
manually, or issue require command. For example, to install Doctrine ORM module to your
web site (to add the doctrine/doctrine-module package to the application dependencies), type
the following:
php composer.phar require doctrine/doctrine-module 2.*

The command above edits composer.json file, and downloads and installs the package. We will
use this command later in Chapter 9, when becoming familiar with database management.

2.12.3 Virtual Packages


Composer can be used to require some functionality to present on your system. Youve already
seen how we require php>=5.3.3. PHP package is a virtual package representing PHP itself. You
can also require other stuff, like PHP extensions:

40

Zend Skeleton Application

Table 2.3. Virtual Composer Packages

Definition Example

Description

php>=5.3.3

Require PHP version greater or equal than 5.3.3

ext-dom, ext-pdo-mysql

Require PHP DOM and PDO MySQL extensions

lib-openssl

Require OpenSSL library

You can use composer show --platform command to display a list of available virtual packages
for your machine.

2.12.4 Composer and Version Control Systems


If you are using a version control system (like SVN), you will be curious about what should
be stored in SVN: your application code only, or your application code plus all the Composerinstalled dependencies in APP_DIR/vendor directory?
In general, it is not recommended to store your Composer-dependencies under version control,
because this can make your repository really big and slow to check out and branch. Instead, you
should store your composer.lock file under version control. The composer.lock file guarantees
that everyone will install the same versions of dependencies as you have. This is useful in
development teams having more than one developer, because all developers should have the
same code to avoid unwanted issues with environment misconfiguration.
What if some dependence will be declared obsolete and removed from Packagist.org?
Well, the possibility of package removal is minimum. All packages are free and opensource, and the community of users can always restore the dependency even if it is
removed from packagist. By the way, the same concept of dependency installation is
used in Linux (remember APT or RPM manager?), so did anyone see any Linux package
lost?

But there may be situations when you should store some dependent libraries under version
control:
If you have to make custom changes to third-party code. For example, assume you have
to fix a bug in a library, and you cannot wait for the librarys vendor to fix it for you (or
if the library vendor cannot fix the bug). In this case, you should place the library code
under version control to ensure your custom changes wont be lost.
If you have written a reusable module or library and want to store it in the vendor directory
without publishing it on Packagist.org. Because you dont have an ability to install this
code from the Packagist, you should store it under version control.
If you want a 100% guarantee that a third-party package wont be lost. Although the risk is
minimum, for some applications it is critical to be autonomous and not depend on package
availability on Packagist.org.

Zend Skeleton Application

41

2.13 Summary
In this chapter, we have downloaded the Zend Skeleton Application project code from GitHub
code hosting and installed it using Composer dependency management tool. Weve configured
the Apache Virtual Host to tell the web server about location of the web sites document root
directory.
The skeleton application demonstrates the recommended directory structure of a typical web
site. We have the public directory containing files publicly accessible by site users, including
the index.php entry point file, CSS files, JavaScript files and images. All other directories of
the application are inaccessible by site users and contain application configuration, data and
modules.
In the second part of the chapter we discussed some advanced usage of hypertext access file
(.htaccess). With this file, you can protect your web site with password and allow accessing it
from certain IP addresses only.
The Composer dependency manager is a powerful tool for installing the dependencies of your
web site. For example, Zend Framework 2 itself can be considered as a dependency. All packages
installable by Composer are registered in a centralized catalog on the Packagist.org site.

3 Web Site Operation


In this chapter we will provide some theory on how a typical Zend Framework 2 based
application works. Youll learn how PHP namespaces are used for avoiding name collisions,
what class autoloading is, how to define application configuration parameters and the stages
present in an applications life-cycle. You will also become familiar with such an important ZF2
components as Zend\EventManager, Zend\ModuleManager and Zend\ServiceManager. If instead
of learning the theory, you want to have some practical examples, skip this chapter and refer
directly to Chapter 4.
ZF2 components covered in this chapter:
Component

Description

Zend\Mvc

Support of Model-View-Controller pattern. Separation of business


logic from presentation.

Zend\Loader

Implements the PHP class autoloading support.

Zend\ModuleManager

This component is responsible for loading and initializing modules of the


web application.

Zend\EventManager

This component implements functionality for triggering events and event


handling.

Zend\ServiceManager

Implements the registry of all services available in the web application.

3.1 PHP Namespaces


When you use classes from different libraries (or even classes from different components of
a single library) in your program, the class names may conflict. This means you can encounter
two classes having the same name, resulting in PHP interpreter error. If youve ever programmed
web sites with Zend Framework 1, you might remember those extra long class names like Zend_Controller_Abstract. The idea with long names was utilized to avoid name collisions between
different components. Each component defined its own name prefix, like Zend_ or My_.
To achieve the same goal, Zend Framework 2 uses the PHP 5.3 language feature called
namespaces. The namespaces allow to solve the name collisions between code components, and
provide you with the ability to make the long names shorter.
A namespace is a container for a group of names. You can nest namespaces into each other.
If a class or function does not define a namespace, it lives inside of the global namespace (for
example, PHP classes Exception and DateTime belong to global namespace).
A real-world example of a namespace definition (taken from ZendMvc component) is presented
below:

Web Site Operation


1
2

43

<?php
namespace Zend\Mvc;

3
4

// ...

5
6
7
8
9
10
11
12

/**
* Main application class for invoking applications.
*/
class Application
{
// ... class members were omitted for simplicity ...
}

You may notice that in example above we have the opening <?php tag which tells the
PHP engine that the text after the tag is a PHP code. In example above, when the file
contains only the PHP code (without mixing PHP and HTML tags), you dont need to
insert the closing ?> tag after the end of the code. Moreover, this is not recommended
and may cause undesired effects, if you occasionally add some character after the
closing ?> tag.

In Zend Framework 2, all classes belong to top-level Zend namespace. The line 2 defines
the namespace Mvc, which is nested into Zend namespace, and all classes of this component
(including the Application class shown in this example on lines 9-12) belong to this namespace.
You separate nested namespace names with the back-slash character (\).
In other parts of code, you reference the Application class using its full name:
<?php
$application = new \Zend\Mvc\Application;

Please note the leading back-slash in \Zend\Mvc\Application name. It represents the


global namespace.

It is also possible to use the alias (short name for the class) with the help of PHPs use statement:
<?php
// Define the alias in the beginning of the file.
use Zend\Mvc\Application;
// Later in your code, use the short class name.
$application = new Application;

Web Site Operation

44

Although the alias allows to use a short class name instead of the full name, its usage
is optional. You are not required to always use aliases, and can refer the class by its full
name.

Every PHP file of your application typically defines the namespace (except index.php entry
script and config files, which typically do not). For example, the main module of your site, the
Application module, defines its own namespace whose name equals to the module name:
<?php
namespace Application;
// ...
class Module
{
// ... class members were omitted for simplicity ...
}

3.2 PHP Interfaces


In PHP, interfaces allow you to define which behavior a class should have, but without providing
the implementation of such a behavior. This is also called a contract: by implementing an
interface, a class agrees to the contract terms.
In Zend Framework 2, interfaces are widely used. For example, the Application class implements
the ApplicationInterface, which defines the methods every application class must provide:
<?php
namespace Zend\Mvc;
//...
interface ApplicationInterface
{
// Retrieves the service manager.
public function getServiceManager();
// Retrieves the HTTP request object.
public function getRequest();
// Retrieves the HTTP response object.
public function getResponse();
// Runs the application.
public function run();
}

Web Site Operation

45

As you can see from the example above, an interface is defined using the interface keyword,
almost the same way you define a standard PHP class. As a usual class, the interface defines
methods. However, the interface does not provide any implementation of its methods. In the
ApplicationInterface interface definition above, you can see that every application implementing this interface will have method getServiceManager() for retrieving the service manager
(about the service manager, see later in this chapter), the getRequest() and getResponse()
methods for retrieving the HTTP request and response, respectively, and method run() for
running the application.
In Zend Framework 2, by convention, interface classes should be named with
Interface suffix, like ApplicationInterface.

A class implementing an interface is called a concrete class. The concrete Application class
implements the ApplicationInterface, which means it provides the implementation of the
methods defined by the interface:
<?php
namespace Zend\Mvc;
//...
class Application implements ApplicationInterface
{
// Implement the interface's methods here
public function getServiceManager() {
//...
}
public function getRequest() {
//...
}
public function getResponse() {
//...
}
public function run() {
//...
}
}

The concrete Application class uses the implements keyword to show that it provides an
implementation of all methods of ApplicationInterface interface. The Application class can
also have additional methods, which are not part of the interface.

46

Web Site Operation

Graphically, the class relations are displayed using inheritance diagrams. In figure 3.1, the
diagram for Application class is presented. The arrow points from the child class to the parent
class.

Figure 3.1. Application class diagram

3.3 PHP Class Autoloading


A web application consists of many PHP classes, and each class typically resides in a separate
file. This introduces the need of including the files.
For example, lets assume we have the file named Application.php which contains the definition
for the \Zend\Mvc\Application class from the previous section. Before you can create an
instance of the Application class somewhere in your code, you have to include the contents
of Application.php file (you can do this with the help of require_once statement, passing it the
full path to the file):
<?php
require_once "/path/to/zend/lib/Application.php";
use Zend\Mvc\Application;
$application = new Application;

As your application grows in size, it may be difficult to include each needed file. Zend Framework
2 itself consists of hundreds of files, and it can be very difficult to load the entire library and all
its dependencies this way. Moreover, when executing the resulting code, PHP interpreter will
take CPU time to process each included file, even if you dont create an instance of its class.
To fix this problem, in PHP 5.1, the class autoloading feature has been introduced. The PHP
function spl_autoload_register() allows you to register an autoloader function. For complex
web sites, you even can create several autoloader functions, which are chained in a stack.
During script execution, if PHP interpreter encounters a class name which has not been defined
yet, it calls all the registered autoloader functions in turn, until either the autoloader function
includes the class or not found error is raised. This allows for lazy loading, when PHP
interpreter processes the class definition only at the moment of class invocation, when it is really
needed.
To give you an idea of how an autoloader function looks like, below we provide a simplified
implementation of an autoloader function:

Web Site Operation

47

<?php
// Autoloader function.
function autoloadFunc($className) {
// Class map static array.
static $classMap = array(
'Zend\\Mvc\\Application' => '/path/to/zend/dir/Zend/Mvc/Application.php',
'Application\\Module' => '/path/to/app/dir/Application/Module.php',
//...
);
// Check if such a class name presents in the class map.
if(isset(static::$classMap[$className])) {
$fileName = static::$classMap[$className];
// Check if file exists and is readable.
if (is_readable($filename)) {
// Include the file.
require $filename;
}
}
}
// Register our autoloader function.
spl_autoload_register("autoloadFunc");

In the above example, we define the autoloadFunc() autoloader function, which we will further
refer to as the class map autoloader.
The class map autoloader uses the class map for mapping between class name and absolute path
to PHP file containing that class. The class map is just a usual PHP array containing keys and
values. To determine the file path by class name, the class map autoloader just needs to fetch
the value from the class map array. It is obvious, that the class map autoloader works very fast.
However, the disadvantage of it is that you have to maintain the class map and update it each
time you add a new class to your program.

3.4 PSR-0 Standard


Because each librarys vendor uses its own code naming and file organization conventions, you
will have to register a different custom autoloader function per each dependent library, which
is rather annoying (and actually this is an unneeded work). To resolve this problem, the PSR-0
standard was introduced.
The PSR-0 standard (PSR stands for PHP Standards Recommendation) defines the recommended
code structure that an application or library must follow to guarantee autoloader interoperability.
In two words, the standard says that:
https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md

Web Site Operation

48

The class namespaces should be organized in the following way:


\<Vendor Name>\(<Namespace>)*\<Class Name>

Namespaces can have as many nesting levels as desired, but the Vendor Name should be
the top-level namespace.
Namespaces should map to directory structure. Each namespace separator (\) is converted
to a OS-specific DIRECTORY_SEPARATOR constant when loading from the file system.
The class name is suffixed with .php extension when loading the file from the file system.
For example, for the Zend\Mvc\Application class, you will have the following directory
structure:
/path/to/zend/lib
/Zend
/Mvc
Application.php

For the code conforming to the PSR-0 standard, we can write and register an autoloader, which
we will refer to as the standard autoloader:
<?php
// "Standard" autoloader function.
function standardAutoloadFunc($className) {
// Replace special characters in class name.
$className = str_replace('\\', '/', $className);
// Format the file path.
$fileName = "path/to/zend/dir/" . $className . ".php";
// Check if file exists and is readable.
if (is_readable($fileName)) {
// Include the file.
require $fileName;
}
}
// Register the autoloader function.
spl_autoload_register("standardAutoloadFunc");

The standard autoloader works as follows. Assuming that the class namespace can be mapped to
the directory structure one-by-one, the function calculates the path to PHP file by transforming
back-slashes (namespace separators) to forward slashes (path separators) and concatenating the
resulting path with the absolute path to the directory where the library is located. Then the
function checks if such a PHP file really exists, and if so, includes it with the require statement.

Web Site Operation

49

It is obvious, that the standard autoloader works slower than the class map autoloader. However,
its advantage is that you dont need to maintain any class map, which is very convenient when
you develop new code and add new classes to your application.
Zend Framework 2 conforms to PSR-0 standard, making it possible to use standard
autoloading mechanism across all its components. It is also compatible with other PSR0 conforming libraries like Doctrine or Symfony 2.

3.5 HTTP Request and Response


When a site user opens a web page in a web browsers window, the browser generates a request
message and sends it using HTTP protocol to the web server. The web server directs this HTTP
request to your web application.
HTTP (stands for Hyper Text Transfer Protocol) a protocol for transferring data in
the form of hyper text documents (web pages). HTTP is based on the client-server
technology: the client initiates a connection and sends a request to web server, and the
server waits for a connection, performs the necessary actions and returns a response
message back.

Thus, the main underlying goal of any web application is handling the HTTP request and
producing an HTTP response typically containing the HTML code of the requested web page.
The response is sent by the web server to the client web browser and the browser displays a web
page on the screen.
A typical HTTP request is presented below:
An HTTP request example
1
2
3
4
5
6
7
8
9
10

GET http://www.w3schools.com/ HTTP/1.1


Host: www.w3schools.com
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64)
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US;q=0.8,en;q=0.6
Cookie: __gads=ID=42213729da4df8df:T=1368250765:S=ALNI_MaOAFe3U1T9Syh;
(empty line)
(message body goes here)

The HTTP request message above consists of three parts:


http://www.w3.org/Protocols/rfc2616/rfc2616.html

Web Site Operation

50

The starting line (line 1) specifies the method of the request (e.g GET or POST), the URL
string and HTTP protocol version.
Optional headers (lines 2-8) characterize the message, the transmission parameters and
provide other meta information. In the example above, each row represents a single header
in the form of name:value.
Optional message body contains message data. It is separated from the headers with a
blank line.
The headers and the message body may be absent, but the starting line is always present in the
request, because it indicates its type and URL.
The server response for the above request is presented below:
An HTTP response example
1
2
3
4
5
6
7
8
9
10
11
12

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/7.5
Set-Cookie: ASPSESSIONIDQQRBACTR=FOCCINICEFAMEKODNKIBFOJP; path=/
X-Powered-By: ASP.NET
Date: Sun, 04 Aug 2013 13:33:59 GMT
Content-Length: 8434
(empty line)
(page content follows)

As you can see from the dump above, the HTTP response has almost the same format as the
request:
The starting line (line 1) represents the HTTP protocol version, response status code and
message (200 OK).
Optional headers (lines 2-10) provide various meta information about the response.
Optional message body follows the headers, and must be separated from headers by an
empty line. The message body typically contains the HTML code of the requested web
page.

3.6 Site Entry Script


When the Apache web server receives an HTTP request from a client browser, it executes the
APP_DIR/public/index.php file, also called the entry script.
The entry script is the only PHP file accessible to the outside world. Apache web server
directs all HTTP requests to this script (remember the .htaccess file?). Having this single
entry script makes the web site more secure (comparing with the situation when you
allow everyone to access all PHP files of your application).

Web Site Operation

51

Although the index.php file is very important, it is surprisingly small (see below):
1

<?php

2
3
4
5
6
7

/**
* This makes our life easier when dealing with paths.
* Everything is relative to the application root now.
*/
chdir(dirname(__DIR__));

8
9
10

// Setup autoloading
require 'init_autoloader.php';

11
12
13
14

// Run the application!


Zend\Mvc\Application::init(
require 'config/application.config.php')->run();

Mainly, there are three things done in it.


First, in line 7, current working directory is changed to APP_DIR. This makes it simple to define
relative file paths in your application:
Next, in line 10, PHP class autoloading is initialized. This allows to easily load any class either
located in Zend Framework library or in your application without the need for require_once
statement.
And finally, in line 13, an instance of Zend\Mvc\Application class is created. The application is
initialized with application.config.php configuration file, and, the application is run.

3.7 Events & Applications Life Cycle


As youve learned from the previous section, on every HTTP request, the Zend\Mvc\Application
object is created. The applications life consists of several stages.
Zend Framework 2 uses the concept of event. One class can trigger an event, and
other classes may listen to events. Technically, triggering an event means just calling
another class callback method. The event management is implemented inside of the
Zend\Mvc\EventManager component.

Each application life stage is initiated by the application by triggering an event. Other classes
(either belonging to Zend Framework or specific to your application) may listen to events and
react accordingly.
Below, the four main events (life stages) are presented:
Bootstrap. When this event is triggered by the application, a module has a chance to register
itself as a listener of further application events in its onBootstrap() callback method.

52

Web Site Operation

Route. When this event is triggered, the requests URL is analyzed using a router class (typically,
with Zend\Mvc\Router\Http\TreeRouteStack class. If an exact match between the URL and a
route is found, the request is passed to the site-specific controller class assigned to the route.
Dispatch. The controller class dispatches the request using the corresponding action method
and produces the data that can be displayed on the web page.
Render. On this event, the data produced by the controllers action method are passed for
rendering to Zend\View\Renderer\PhpRenderer class. The renderer class uses a view template
file for producing an HTML page.
The event flow is illustrated in figure 3.2:

Figure 3.2. Event flow during the applications life cycle

3.8 Application Configuration


Most of Zend Framework 2 components which are used in your web site, require configuration
(fine-tuning). For example, in the configuration file you define database connection credentials,
specify which modules are present in your application, and, optionally, provide some custom
parameters specific to your application.
You can define the configuration parameters in two levels: either at the application level, or at
the module level. At the application level you typically define parameters which control the
whole app and are common to all modules of your application. At the module level, you define
parameters which affect only this module.

53

Web Site Operation

Some PHP frameworks prefer conventions over configuration concept, where most of
your parameters are hard-coded and do not require configuration. This makes it faster
to develop the application, but makes it less customizable. In Zend Framework 2, the
configuration over conventions concept is used, so you can customize any aspect of
your application, but have to spend some time for learning how to do that.

3.8.1 Application-Level Config Files


The APP_DIR/config subdirectory contains application-wide configuration files. Lets look at this
subdirectory in more details (figure 3.3).

Figure 3.3. Configuration files

The APP_DIR/config/application.config.php file is the main configuration file. It is used by the


application on start up for determining which services should be created in the service manager
and which application modules should be loaded.
Below, a starting fragment of application.config.file is presented. You can see that the configuration file is just a usual PHP nested associative array, and each component may have a specific
key in that array. You can provide inline comments for the array keys to make it easier for others
to understand what each key means.
By convention, key names should be in lower case, and if the key name consists of
several words, the words should be separated by the underscore symbol (_).

Web Site Operation

54

Content of application.config.php file


1
2
3
4
5
6
7

<?php
return array(
// This should be an array of module namespaces
// used in the application.
'modules' => array(
'Application',
),

8
9
10
11
12
13
14
15
16
17
18
19
20

// These are various options for the listeners


// attached to the ModuleManager
'module_listener_options' => array(
// This should be an array of paths in which
// modules reside. If a string key is provided,
// the listener will consider that a module
// namespace, the value of that key the specific
// path to that module's Module class.
'module_paths' => array(
'./module',
'./vendor',
),

21

// An array of paths from which to glob configuration


// files after modules are loaded. These effectively
// override configuration provided by modules themselves.
// Paths may use GLOB_BRACE notation.
'config_glob_paths' => array(
'config/autoload/{,*.}{global,local}.php',
),

22
23
24
25
26
27
28
29

// ...

30
31

);

In line 5 we have the modules key listing all modules which are present in your web site.
Currently, you have the single Application module.
In line 17, there is the module_paths key which tells ZF2 about directories where to look for
source files belonging to modules. Application modules that you develop are located under
APP_DIR/module directory, and third-party modules may be located inside the APP_DIR/vendor
directory.
And in line 26 we have the config_glob_paths key, which tells ZF2 where to look for extra
config files. You see that files from APP_DIR/config/autoload which have global.php or local.php
suffix, are automatically loaded.
Summing up, you typically use the main application.config.php file for storing the information
about which modules should be loaded into your app and where they are located and how they

Web Site Operation

55

are loaded (for example, you can control caching options here). In this file you can also tune the
service manager. It is not recommended to add more keys in this file. For that purpose it is better
to use autoload/global.php file.

3.8.2 Application-Level Extra Config Files


Extra config files, APP_DIR/config/autoload/global.php and APP_DIR/config/autoload/local.php
files define application-wide environment-agnostic and environment-dependent parameters,
respectively. These config files are automatically loaded and recursively merged with the
module-provided config files, thats why their directory is named autoload.
Having different config files in APP_DIR/config/autoload directory, you might have been
confused about which parameters should be put into each one. Here are some hints:
You use the autoload/global.php file for storing parameters which do not depend on
the concrete machine environment. For example, here you can store parameters which
override the default parameters of some module. Do not store confidential information
(like database credentials) here, for that purpose its better to use autoload/local.php.
You use the autoload/local.php file for storing parameters specific to the concrete environment. For example, here you can store your database credentials. Each developer usually
has a local database when developing and testing the web site. The developer thus will
edit the local.php file and enter his own database credentials here. When you install your
site to the production server, you will edit the local.php file and enter the credentials for
the live database here.
Because the autoload/local.php file contains environment-specific parameters, in version control system you store its distribution template local.php.dist. Each developer
in your team then renames the local.php.dist file into local.php and enters his own
parameters. This local.php file should not be stored under version control, because
it may contain confidential information like database credentials (username and
password), and you might want that other people do not see these.

3.8.3 Module-Level Config Files


In figure 3.3, you can see that the Application module shipped with your application has the
module.config.php file, in which you put your module-specific parameters.

Web Site Operation

56

module.config.php file

<?php
return array(
'router' => array(
'routes' => array(
// Register URL routing rules here.
),
),
'service_manager' => array(
// Register module-provided services here.
),
'controllers' => array(
// Register module-provided controllers here.
),
'view_helpers' => array(
// Register module-provided view helpers here.
),
'view_manager' => array(
// Provide view manager configuration here.
),
);

In this file, you register the modules controllers, put information about routing rules for mapping
URLs to your controllers, register controller plugins, and also register view templates and view
helpers (we will learn more about these terms in this chapter and in the next chapters).

3.8.4 Combining the Configuration Files


When an application is created, module-provided configuration files and extra configuration
files from APP_DIR/config/autoload directory are merged into one big nested array, so every
configuration parameter becomes available to any piece of the web site. So, potentially, you are
able to override some parameters specified by the modules.
You might also have seen the combined config file when installing PHP, where there
is the main php.ini file and several extra config files, which are included into the main
one. Such a separation makes your application configuration fine-grained and flexible,
because you dont have to put all your params to a single file and edit it each time you
need to change something.

The configuration files are loaded in the following order:


The main application.config.php file is loaded first. It is used to initialize the service
manager and load application modules. The data loaded from this config is stored alone
and not merged with other config files.

Web Site Operation

57

Configuration files for each application module are loaded and merged. Modules are
loaded in the same order as they are listed in the application.config.php file. If two modules
store (either intentionally, or by mistake) parameters in the similar-named keys, these
parameters may be overwritten.
Extra config files from the APP_DIR/config/autoload folder are loaded and merged into
a single array. Then this array is merged with the module config array produced on the
previous stage, when loading the module configuration. Application-wide configuration
has higher priority than the module configuration, so you can override module keys here,
if you wish.

3.9 Module Entry Point


Each module of the web application has the Module.php file which is some kind of entry point
for the module. This file provides the Module class with several callback methods. Below, the
contents of skeleton applications Module class is presented:
Contents of Module.php file
1
2

<?php
namespace Application;

3
4
5

use Zend\Mvc\ModuleRouteListener;
use Zend\Mvc\MvcEvent;

6
7
8
9
10
11
12
13
14

class Module
{
public function onBootstrap(MvcEvent $e)
{
$eventManager = $e->getApplication()->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
}

15
16
17
18
19

public function getConfig()


{
return include __DIR__ . '/config/module.config.php';
}

20
21
22
23
24
25
26
27
28

public function getAutoloaderConfig()


{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),

Web Site Operation

);

29

30
31

58

The class Module belongs to the modules namespace (for the main module it belongs to the
Application namespace).
The onBootstrap() method (lines 9-14) is called by the application on start up, during the
Bootstrap event, and allows to register event listeners for the module.
The getConfig() method (lines 16-19) tells the Zend Framework where the module.config.php
file is located.
The getAutoloaderConfig() method (lines 21-30) tells the Zend Framework where to find the
source files for the module. This information is used for initializing the autoloader function for
the module.

3.10 Class Autoloading in Zend Framework 2


Now that we know about PHP namespaces, class autoloading basics and application configuration, we can learn in more details about how class autoloading works in a Zend Framework 2
based application.

3.10.1 Composer-provided Autoloader


Everything begins with the Composer dependency manager. When you install a package with
Composer, it automatically creates the file APP_DIR/vendor/autoloader.php, which uses the
spl_autoload_register() PHP function to register an autoloader.
Recalling the index.php site entry script from a previous section, we see that it includes the
autoloader initialization script file named init_autoloader.php. The init_autoloader.php script
does one simple thing. It checks if Composers autoloader file is present, and just includes that
file:
<?php
//...
// Composer autoloading
if (file_exists('vendor/autoload.php')) {
$loader = include 'vendor/autoload.php';
}

This makes it possible to automatically find and load any PHP class in any library installed with
Composer (including Zend Framework 2 classes).

Web Site Operation

59

By default, Composer uses the PSR-0 standard autoloader. To improve autoloading


performance, especially for production environment, you may want to enable class
map autoloader instead. You can do that by specifying the --optimize-autoloader
option for Composers install and update commands, like below:
php composer.phar install --optimize-autoloader
php composer.phar update --optimize-autoloader

3.10.2 Zend\Loader Component


Each module of the web application registers an autoloader, which makes it possible to autoload
any PHP class in your modules. This is made with the getAutoloaderConfig() method of the
Module class.
ZF2 has a special component named Zend\Loader, which contains implementations of the two
commonly-used autoloader classes: the standard autoloader (Zend\Loader\StandardAutoloader)
and class map autoloader (Zend\Loader\ClassMapAutoloader).
The autoloader class hierarchy is displayed in the diagram below (figure 3.4). The StandardAutoloader
and ClassMapAutoloader classes both implement SplAutoloader interface:
// Defines an interface for classes that may register with the spl_autoload
// registry
interface SplAutoloader
{
// Constructor
public function __construct($options = null);
// Configure the autoloader
public function setOptions($options);
// Autoload a class
public function autoload($class);
// Register the autoloader with spl_autoload registry
public function register();
}

The SplAutoloader interface defines the register() method, which is intended for registering
the autoloader function (class method in our case) with the help of spl_autoloader_register(),
and the autoload() method, which is intended for providing the concrete class discovery
algorithm.

60

Web Site Operation

Figure 3.4. ZF2 autoloader class diagram

3.10.2.1 Standard Autoloader


The fact that ZF2-based application modules conform to PSR-0 standard makes it possible to use
the standard autoloader.
In the skeleton applications Module class, the getAutoloaderConfig() method provides the
configuration for registering the source PHP files belonging to the modules namespace within
the standard autoloader:
<?php
namespace Application;
class Module {
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
}

In the above method, we see that the module tells ZF2 to use the Zend\Loader\StandardAutoloader
for autoloading the modules source files. The namespaces key contains keyvalue pairs, where
key is the namespace, and the value is the absolute path to the directory containing the PHP files
implementing the classes from that namespace. The __NAMESPACE__ constant expands into the
modules namespace string (for example, to Application for the main module of the application),
and the __DIR__ constant expands to directory path where the Module.php file is located.

Web Site Operation

61

3.10.2.2 PSR-0 and Src Directory Structure


In Zend Skeleton Application web site, you can see how the PSR-0 standard is applied in practice.
For the default module of your web site, the Application module, PHP classes which are
registered with the standard autoloader are stored under the APP_DIR/module/Application/src
directory (src abbreviation means source).
We will refer to the src directory as modules source directory.

For example, lets look at the IndexController.php file of Application module (figure 3.5).

Figure 3.5. Skeleton applications directory structure conforms to PSR-0 standard

As you can see, it contains the IndexController class belonging to Application\Controller


namespace. To be able to follow the PSR-0 standard and use the standard autoloader with this
PHP class, we have to put it under the Application/Controller directory under the modules
source directory.
At a first sight, the duplication of Application directory name in directory structure
would look strange and unneeded if you do not keep in mind the PSR-0 standard
requirements.

IndexController class is the default controller for the skeleton web site. We will talk about controllers later in Chapter 4 when learning
about Model-View-Controller pattern.

Web Site Operation

62

3.10.2.3 Class Map Autoloader


The class map autoloader can be used as a faster replacement for the standard autoloader. This
autoloader expects you to pass it a class map array. Each keyvalue pair of the class map is,
respectively, the class name and path to the PHP file containing the class.
The modules getAutoloaderConfig() method would look like below:
1
2

<?php
namespace Application;

3
4

class Module {

//...

6
7

public function getAutoloaderConfig()


{
return array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoloader_classmap.php', // File-based class map.
array(
// Array class map.
'Application\Controller\IndexController' =>
__DIR__ . 'src/Application/Controller/IndexController.php,
//...
),
),
);
}

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

In the example above, we can provide the class map using an array (see lines 13-15) containing
keyvalue pairs, where the key is the full class name, and value is the absolute path to PHP
file where the class is implemented. Or we can create the class map in an external PHP file and
provide the autoloader the path to that file (line 12).
The problem with using the class map autoloader is that you have to maintain the class map,
which is difficult to do by hands. Fortunately, Zend Framework 2 provides you with the special
tool for this. The tool is a PHP script located in APP_DIR/vendor/bin/classmap_generator.php
file. To generate a class map file for your module, you call the generator script like below:
cd APP_DIR/vendor/bin/
php classmap_generator.php -l path/to/module -o /output/dir

3.11 Service Manager


You can imagine the web application as a set of services. For example, you can have an
authentication service responsible for logging in the site users, entity manager service responsible

63

Web Site Operation

for accessing the database, event manager service responsible for triggering events and delivering
them to event listeners, etc.
In Zend Framework 2, the ServiceManager class is a centralized repository for all application
services. The service manager is implemented in Zend\ServiceManager component, as the
ServiceManager class, which implements the ServiceLocatorInterface interface. The class
diagram is presented in figure 3.6:

Figure 3.6. ServiceManager class inheritance

The service manager is created on application start up (inside of init() static method of
Zend\Mvc\Application class). The standard services available through service manager are
presented in table 3.1. This table is incomplete, because the actual number of services registered
in service manager may be much bigger.
Service Name

Description

Application

Allows to retrieve the singleton of Zend\Mvc\Application class.

ApplicationConfig

Configuration array extracted from application.config.php file.

Config

Merged configuration array extracted from module.config.php files


merged with autoload/global.php and autoload/local.php.

EventManager

Allows to retrieve the singleton of Zend\Mvc\EventManager class. The


event manager allows to send (trigger) events and attach event listeners.

ModuleManager

Allows to retrieve the singleton of Zend\Mvc\ModuleManager class. The


module manager is responsible for loading application modules.

Request

The singleton of Zend\Http\Request class. Represents HTTP request


received from client.

Response

The singleton of Zend\Http\Response class. Represents HTTP response that


will be sent to client.

Router

The singleton of Zend\Mvc\Router\Http\TreeRouteStack. Performs URL


routing.

ServiceManager

Service manager itself.

ViewManager

The singleton of Zend\Mvc\View\Http\ViewManager class. Responsible for


preparing the view layer for page rendering.

A service is typically an arbitrary PHP class, but not always. For example, when ZF2 loads the
configuration files and merges the data into nested arrays, it saves the arrays in the service

Web Site Operation

64

manager as a couple of services (!): ApplicationConfig and Config. The first one is the array
loaded from application-level configuration file application.config.php, and the latter one is the
merged array from module-level config files and auto-loaded application-level config files. Thus,
in the service manager you can store any asset you want: a PHP class, a variable or an array.
From table 3.1, you can see that in ZF2 everything can be considered as a service. The service
manager is itself registered as a service. Moreover, the Application class is also registered as a
service.
An important thing you should note about the services is that they are typically stored
in a single instance only (this is also called the singleton pattern). Obviously, you dont
need the second instance of the Application class or, say, the event manager (in that
case you would have a nightmare with proliferating events).

3.11.1 Service Locator


As you can see from figure 3.5, the service manager implements the ServiceLocatorInterface
interface. As its name suggests, this interface defines the minimum needed methods for locating
and retrieving a service from the service manager:
<?php
namespace Zend\ServiceManager;
interface ServiceLocatorInterface {
// Retrieves a registered service's instance.
public function get($name);
// Checks if such a service is registered.
public function has($name);
}

In the web application, you typically deal with the ServiceLocatorInterface, instead of the
service manager itself.
You can test if a service is registered by passing its name to the service locators has() method.
It returns a boolean true if the service is registered, or false if the service with such a name is
not registered.
You can retrieve a service by its name at any place of your application with the help of the
service locators get() method. This method takes a single parameter representing the service
name. Look at the following example:

Web Site Operation

65

<?php
// Retrieve the application config array.
$appConfig = $serviceLocator->get('ApplicationConfig');
// Use it (for example, retrieve the module list).
$modules = $appConfig['modules'];

In the example above, we assume you have already retrieved the service locator from
somewhere. Looking ahead, lets say that typically, you will do that in your controller
classes with the help of getServiceLocator() method. We will learn about controller
classes later in Chapter 4.

3.11.2 Canonical Service Names


Different services can use different naming styles: the same currency converter service may be
registered under the different names: CurrencyConverter, currency_converter and so on. To
introduce some uniform naming convention, the service manager internally uses the canonical
service names.
A canonical service name consists of lower-case letters. The space, forward- and back-slashes,
underscore and minus sign characters are not allowed. To produce the canonical name, the
service manager uses its canonicalizeName() method, which takes the usual service name string
and produces the canonical name string.
To get the list of all canonical service names registered within the service manager, you can use
its getCanonicalNames() method, which returns an array of string pairs. The first element of the
pair is the canonical service name. The second one is the usual name of the service (the name
under which it was registered).

3.11.3 Registering a Service


When writing your web site, sometimes you will need to register your custom service in the
service manager. To register a service, you use the setService() method. Lets create and register
the currency converter service class, which will be used, for example, on a shopping cart page to
convert EUR currency to USD:

Web Site Operation


1
2
3

66

<?php
// Define a namespace where our custom service lives.
namespace Application\Service;

4
5
6

// Define a currency converter service class.


class CurrencyConverter {

// Converts euros to US dollars.


public function convertEURtoUSD($amount) {
return $amount*1.25;
}

8
9
10
11
12

//...

13
14

15
16
17
18
19

// Create an instance of the class.


$service = new CurrencyConverter();
// Save the instance to service manager.
$serviceManager->setService('CurrencyConverter', $service);

Above, in lines 5-13 we define an example CurrencyConverter class (for simplicity, we implement only a single method convertEURtoUSD() which is able to convert euros to US dollars).
Next, in line 17 we instantiate the class with the new operator, and in line 19 we register it with the
service manager using the setService() method (we assume that the $serviceManager variable
is of type Zend\ServiceManager\ServiceManager class, and that it was declared somewhere
else).
The setService() method takes two parameters: the service name string, and the service
instance. The service name should be unique within all other possible services. If you are trying
to register the service name which is already present, the setService() method throws an
exception. But sometimes you want to override the service with the same name (to replace it
by the new one). For this purpose, you can use the setAllowOverride() method of the service
manager:
1
2
3
4
5
6

<?php
// Allow to replace services
$serviceManager->setAllowOverride(true);
// Save the instance to service manager. There will be no exception
// even if there is another service with such a name.
$serviceManager->setService('CurrencyConverter', $service);

Above, the setAllowOverride() method takes the single boolean parameter defining whether
to allow you replace the service CurrencyConverter if such a name is already present, or not.
Once the service is stored in service manager, you can retrieve it by name at any place of your
application with the help of the service managers get() method. Look at the following example:

Web Site Operation

67

<?php
// Retrieve the currency converter service.
$service = $serviceManager->get('CurrencyConverter');
// Use it (convert money amount).
$convertedAmount = $service->convertEURtoUSD(50);

3.11.4 Registering Invokable Classes


What is bad with the setService() method is that you have to create the service instance
before you really need it. If you never use the service, the service instantiation will only
waste the time and memory. To resolve this issue, the service manager provides you with the
setInvokableClass() method.
<?php
// Register an invokable class
$serviceManager->setInvokableClass('\Application\Service\CurrencyConverter');

In the example above, we pass to the service manager the full class name of the service instead
of passing its instance. With this technique, the service will be instantiated by the service
manager only when someone calls the get('CurrencyConverter') method. This is also called
lazy loading.

3.11.5 Registering Factories


Sometimes, service instantiation is more complex than just creating the service instance with
new operator. You may need to pass some parameters to the services constructor or invoke some
service methods just after construction. This complex instantiation logics can be encapsulated
inside of a factory. A factory is a PHP class responsible for creating new objects. The factory
class typically implements the FactoryInterface:
<?php
namespace Zend\ServiceManager;
// Factory interface.
interface FactoryInterface
{
// Creates the service instance.
public function createService(ServiceLocatorInterface $serviceLocator);
}

As we see from the definition of the FactoryInterface, the factory class must provide the
createService() method returning the instance of a single service. The service locator is passed
to the createService() method as a parameter; it can be used during the construction of the
service for accessing other services.
As an example, lets write a factory for our currency converter service (see the code below). We
dont use complex construction logics for our CurrencyConverter service, but for more complex
services, you may need to use one.

Web Site Operation

68

<?php
use Zend\ServiceManager\FactoryInterface;
// Factory class
class CurrencyConverterFactory implements FactoryInterface{
public function createService(ServiceLocatorInterface $serviceLocator) {
// Create an instance of the class.
$service = new CurrencyConverter();
return $service;
}
}

Even more complex case of a factory is when you need to determine at run time which services
should be registered, and which should not. For such a situation, you can use an abstract factory.
An abstract factory class should implement the AbstractFactoryInterface interface:
<?php
namespace Zend\ServiceManager;
interface AbstractFactoryInterface
{
// Determine if we can create a service with such a name.
public function canCreateServiceWithName(
ServiceLocatorInterface $serviceLocator, $name, $requestedName);
// Create service with such a name.
public function createServiceWithName(
ServiceLocatorInterface $serviceLocator, $name, $requestedName);
}

The AbstractFactoryInterface interface has two methods: canCreateServiceWithName() and


createServiceWithName(). The first one is needed to test if the factory can create the service
with the certain name, and the latter one allows to actually create the service. The methods
take three parameters: service locator, canonical service name and usual service name (as it was
requested from service locator by the client).
Comparing to usual factory class, the difference is that the usual factory class can create only a
single service, but an abstract factory can dynamically create as many services as it wants.

3.11.6 Registering Service Aliases


Sometimes, you may want to define an alias for a service. The alias is like a symbolic link:
it references the already registered service. To create an alias, you use the service managers
setAlias() method:

Web Site Operation

69

<?php
// Register an alias for the CurrencyConverter service
$serviceManager->setAlias('CurConv', 'CurrencyConverter');

Once registered, you can retrieve the service by both its name and alias using the service locators
get() method.

3.11.7 Service Manager Configuration


To automatically register a service within the service manager, typically the service_manager
key of a configuration file is used. You can put this key either inside of an application-level
configuration file or in a module-level configuration file.
If you are putting this key in a module-level configuration file, be careful about the
danger of name overwriting during the configs merge. Do not register the same service
name in different modules.

This service_manager key should look like below:


1
2
3

<?php
return array(
//...

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

// Register the services under this key


'service_manager' => array(
'services' => array(
// Register service class instances here
//...
),
'invokables' => array(
// Register invokable classes here
//...
),
'factories' => array(
// Register factories here
//...
),
'abstract_factories' => array(
// Register abstract factories here
//...
),
'aliases' => array(
// Register service aliases here
//...
),

Web Site Operation


27

70

),

28
29
30

//...
);

In the example above, you can see that the service_manager key may contain several subkeys
for registering services in different ways:
the services subkey (line 7) allows to register class instances;
the invokables subkey (line 11) allows to register full class name of a service; the service
will be instantiated using lazy loading;
the factories subkey (line 15) allows for registering a factory, which is able to create
instances of a single service;
the abstract_factories (line 19) can be used for registering abstract factories, which are
able to register several services by name;
the aliases subkey (line 23) provides an ability to register an alias for a service.
As another example, lets look how this key looks like in the module.config.php file of the
Application module of the skeleton application:
<?php
return array(
//...
'service_manager' => array(
'abstract_factories' => array(
'Zend\Cache\Service\StorageCacheAbstractServiceFactory',
'Zend\Log\LoggerAbstractServiceFactory',
),
'aliases' => array(
'translator' => 'MvcTranslator',
),
),
);

Above, we can see that there are two abstract factories registered (for storage cache service and
logger service) and an alias is registered for the translator service. We will become familiar with
these services in the next chapters of this book.

3.12 Summary
In this chapter, weve learned some theory about ZF2-based web site operation basics.
ZF2 uses PHP namespaces and class autoloading features, simplifying the development of
applications which use many third-party components. The namespaces allow to solve the name

Web Site Operation

71

collisions between code components, and provide you with the ability to make the long names
shorter.
The class autoloading makes it possible to use any PHP class in any library installed with
Composer without the use of require_once statement.
Most of Zend Framework 2 components require configuration. You can define the configuration
parameters either at the application level, or at the module level.
The main goal of any web application is handling the HTTP request and producing an HTTP
response typically containing the HTML code of the requested web page. When Apache web
server receives an HTTP request from a client browser, it executes the index.php file, which is
also called the sites entry script. On every HTTP request, the Zend\Mvc\Application object is
created, whose life cycle consists of several stages (or events).
The web application can be also considered as a set of services. In Zend Framework 2, the service
manager is a centralized repository for all the application services. A service is typically a PHP
class, but in general it can be a variable or an array, if needed. In your code, you retrieve the
services from the service manager with the help of the service locator interface.

4 Model-View-Controller
In this chapter, you will learn about the models, views and controllers (MVC). The web
application uses the MVC pattern to separate business logic from presentation. The goal of this
is to allow for code reusability and separation of concerns.
ZF2 components covered in this chapter:
Component

Description

Zend\Mvc

Support of MVC pattern. Implements base controller classes, controller plugins, etc.

Zend\View

Implements the functionality for variable containers, rendering a web page and
common view helpers.

Zend\Http

Implements a wrapper around HTTP request and response.

Zend\Version

A small auxiliary component, which can be used for checking the version of Zend
Framework.

4.1 Get the Hello World Example from GitHub


In this and in the next chapters, we will provide some code examples that you may want to
reproduce yourself. It may be difficult for a novice to write code without mistakes. If you are
stuck or can not understand why your code does not work, you can download the complete Hello
World web application from GitHub code hosting. The examples from this chapter are mostly
the part of this sample application.
To download the Hello World application, visit this page and click the Download ZIP button to
download the code as a ZIP archive (see figure 4.1). When download is complete, unpack the
archive to some directory.
Then navigate to the helloworld directory containing the complete source code of the Hello
World example:
/using-zend-framework-2-book
/helloworld
...

The Hello World is a complete web site which can be installed on your machine. To install the
example, you can either edit your default virtual host file or create a new one. After editing the
file, restart the Apache HTTP Server and open the web site in your web browser.
https://github.com/olegkrivtsov/using-zend-framework-2-book

Model-View-Controller

73

4.2 Separating Business Logic from Presentation


A typical web site has three kinds of functionality: code implementing business logic, code
implementing user interaction and code rendering HTML pages (presentation). Before PHP
frameworks, programmers usually merged these three types of code in a single big PHP script
file, which made it a pain to test and maintain such a code, especially when you write a large
web site.

Figure 4.1. The Hello World sample can be downloaded from GitHub

Since that time, PHP became object-oriented, and now you can organize your code into classes.
The Model-View-Controller (MVC) pattern is just a set of advices telling you how to organize
your classes in a better manner, to make them easy to maintain.
In MVC, classes implementing your business logic are called models, code snippets rendering
HTML pages are called views, and the classes responsible for interacting with user are called
controllers.
Views are implemented as code snippets, not as classes. This is because views are
typically very simple and contain only the mixture of HTML and inline PHP code.

Model-View-Controller

74

The main objective of the MVC concept is to separate the business logic (models) from its
visualization (views). This is also called the separation of concerns, when each layer does its
specific tasks only.
By separating your models from views, you reduce the number of dependencies between them.
Therefore, changes made to one of the layers have the lowest possible impact on other layers.
This separation also improves the code reusability. For example, you can create multiple visual
representations for the same models.
To better understand how this works, lets remember that any web site is just a PHP program
receiving an HTTP request from the web server, and producing an HTTP response. Figure 4.2
shows how an HTTP request is processed by the MVC application and how the response is
generated:
First, the site visitor enters an URL in his web browser, for example http://localhost, and
the web browser sends the request to the web server over the Internet.
Web servers PHP engine runs the index.php entry script. The only thing the entry script
does is creating the Zend\Mvc\Application class instance.
The application uses its router component for parsing the URL and determining to which
controller to pass the request. If the route match is found, the controller is instantiated and
its appropriate action method is called.
In the controllers action method, parameters are retrieved from GET and POST variables.
To process the incoming data, the controller instantiates appropriate model classes and
calls their methods.
Model classes use business logic algorithms to process the input data and return the
output data. The business logic algorithms are application-specific, and typically include
retrieving data from database, managing files, interacting with external systems and so
on.
The result of calling the models are passed to the corresponding view script for the
rendering of the HTML page.
View script uses the model-provided data for rendering the HTML page.
Controller passes the resulting HTTP response to application.
Web server returns the resulting HTML web page to the users web browser.
The user sees the page in browser window.
Now you might have some idea how models, views and controllers cooperate to generate HTML
output. In the next sections, we describe them in more details.

Model-View-Controller

75

Figure 4.2. HTTP request processing in an MVC web application

4.3 Controllers
A controller provides communication between the application, models and views: gets input
from HTTP request and uses the model(s) and the corresponding view to produce the necessary
HTTP response.
Controllers belonging to module typically reside in the Controller subdirectory of modules
source directory (shown in figure 4.3).

76

Model-View-Controller

Figure 4.3. Controller directory

Zend Skeleton Application provides you with the default implementation of IndexController
class. The IndexController is typically the main controller class of the web site. Its code is
presented below (some parts of code were omitted for simplicity):
1
2
3

<?php
// IndexController.php
namespace Application\Controller;

4
5
6

use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;

7
8

class IndexController extends AbstractActionController {

// The "index" action


public function indexAction() {

10
11
12

return new ViewModel();

13

14
15

From the example above, you can see that controllers usually define their own namespace
(line 3). The Index controller, as all other controllers from the Application module, lives in
Application\Controller namespace.
A controller is a usual PHP class derived from the AbstractActionController base class (line
8).
By default, the controller class contains the single action method called indexAction() (see lines
11-14). Typically, you will create other action methods in your controller classes.
ZF2 automatically recognizes the action methods by the Action suffix. If a controller
methods name does not have that suffix, it is considered as a usual method, not an
action.

77

Model-View-Controller

As its name assumes, an action method performs some site action, which typically results in
displaying a single web page. Index controller usually contains action methods for site-wide
web pages (table 4.1). For example, you would have index action for the Home page, about
action for About page, contactUs action for the Contact Us page and possibly other actions.
Table 4.1. Index controllers typical actions

Action Method

Description

IndexController::indexAction()

The index action displays the Home page of your


site.

IndexController::aboutAction()

The about action displays the About page of


the site. The About page contains contact and
copyright information.

IndexController::contactUsAction()

The contactUs action displays the Contact Us


page of the site. The Contact Us page displays
the form for contacting site authors.

4.3.1 Base Controller Class


Every controller in your web site is inherited from the AbstractActionController base class.
In figure 4.4, the class inheritance diagram is presented.

Figure 4.4. Controller inheritance diagram

The AbstractActionController provides you with several useful methods you can use in your
controller classes. Table 4.2 provides you with a brief summary of the methods:
Table 4.2. AbstractActionControllers useful methods

Method Name

Description

getRequest()

Retrieves the Zend\Http\Request object, which is the


representation of HTTP request data.

getResponse()

Retrieves the Zend\Http\PhpEnvironment\Response object


allowing to set data of HTTP response.

getServiceLocator()

This method returns the Zend\ServiceManager\ServiceLocator


interface, allowing to access all services registered in the
web application.

78

Model-View-Controller

Table 4.2. AbstractActionControllers useful methods

Method Name

Description

getEventManager()

Returns the Zend\EventManager\EventManager object,


allowing to trigger events and listen to events.

getEvent()

Returns the Zend\Mvc\MvcEvent object, which represents


the event the controller responds to.

getPluginManager()

Returns the Zend\Mvc\Controller\PluginManager object,


which can be used for registering controller plugins.

plugin($name, $options)

This method allows to access certain controller plugin


with the given name.

__call($method, $params)

Allows to call a plugin indirectly using the PHP __call


magic method.

As you can see from the table above, the base controller class provides you with access to HTTP
request and response data, and provides you with the access to the service manager and to the
event manager. It also gives you an ability to register and call controller plugins (we will learn
about controller plugins later in this chapter).

4.3.2 Retrieving Data from HTTP Request


In a controllers action method, you may need to retrieve the data from the HTTP request (the
data like GET and POST variables, cookies, HTTP headers and so on). For this purpose, Zend
Framework 2 provides you with Zend\Http\Request and Zend\Http\PhpEnvironment\Response
classes, which are part of Zend\Http component.
To get the HTTP request object, inside of your action method, you can use the following code:
// Get HTTP request object
$request = $this->getRequest();

The code above returns the instance of Zend\Http\Request class, containing all the HTTP
request data. In table 4.3, you can find the most widely used methods of the Request class together
with their brief description.
Method Name

Description

isGet()

Checks if this is a GET request.

isPost()

Checks if this is a POST request.

isXmlHttpRequest()

Checks if this request is an AJAX request.

isFlashRequest()

Check if this request is a Flash request.

getMethod()

Returns the method for this request.

getUriString()

Returns the URI for this request object as a string.

79

Model-View-Controller

Method Name

Description

getQuery($name, $default)

Returns the query parameter by name, or all query parameters.


If a parameter is not found, returns the $default value.

getPost($name, $default)

Returns the parameter container responsible for post


parameters or a single post parameter.

getCookie()

Returns the Cookie header.

getFiles($name, $default)

Returns the parameter container responsible for file


parameters or a single file.

getHeaders($name, $default)

Returns the header container responsible for headers


or all headers of a certain name/type.

getHeader($name, $default)

Returns a header by $name. If a header is not found,


returns the $default value.

renderRequestLine()

Returns the formatted request line (first line) for


this HTTP request.

fromString($string)

A static method that produces a Request object from a


well-formed Http Request string

toString()

Returns the raw HTTP request as a string.

4.3.3 Retrieving GET or POST Variables


To simply get a GET or POST variable from an HTTP request, you use the following code:
1
2

// Get a variable from GET


$getVar = $this->params()->fromQuery('var_name', 'default_val');

3
4
5

// Get a variable from POST


$postVar = $this->params()->fromPost('var_name', 'default_val');

In the example above, we used the Params controller plugin, which provides you with convenient
methods of accessing GET and POST variables, uploaded files, etc.
In line 2 we use the fromQuery() method for retrieving a variable having name var_name from
GET. If such a variable does not present, the default value default_val is returned. The default
value is very convenient, because you dont have to use the isset() PHP function to test if the
variable exists.
In line 5 we use the fromPost() method to retrieve the variable from POST (line 5). The meaning
of this methods parameters is the same as for the fromQuery() method.
In ZF2, you must not access request parameters through traditional PHP $_GET and
$_POST global arrays. Instead, you use ZF2-provided API for retrieving the request
data.

80

Model-View-Controller

4.3.4 Putting Data to HTTP Response


Although you typically do not interact with HTTP response data directly, you can do that with
the help of getResponse() method provided by AbstractActionController base class. Table 4.4
contains the most important methods of the Response class:
Table 4.4. Methods of Zend\Http\PhpEnvironment\Response class.

Method Name

Description

fromString($string)
getCookie()
setStatusCode($code)
getStatusCode()
setReasonPhrase($reasonPhrase)
getReasonPhrase()
getBody()
isForbidden()
isNotFound()
isOk()
isServerError()
isRedirect()
isSuccess()
renderStatusLine()
toString()

Populate response object from string.


Retrieves Cookie header.
Sets HTTP status code and (optionally) message.
Retrieves HTTP status code.
Sets the HTTP status message.
Gets HTTP status message.
Gets the body of the response.
Checks if the response code is 404 Forbidden.
Checks if the status code indicates the resource is not found.
Checks whether the response is successful.
Checks if the response is 5xx status code.
Checks whether the response is 303 Redirect.
Checks whether the response is 200 Successful.
Renders the status line header.
Renders entire response as HTTP response string.

4.4 Variable Containers


After you retrieve the data from the HTTP request (or from other data sources, for example, from
database), you would do something with that data (typically you will process the data with your
model layer) and return the data from the action method.
You can see that the indexAction() method of the Index controller returns an instance of the
ViewModel class. The ViewModel class is some kind of a variable container. All variables passed
to its constructor, will be then automatically accessible by the view script.
Lets have some real-life example. We will create another action method in our IndexController
class, which we will call the aboutAction(). The about action will display the About page of
our site. In the action method, we will get the current version of the Zend Framework with the
Zend\Version component, and return the resulting variables for rendering in a view with the
help of ViewModel object:

Model-View-Controller
1
2
3

81

// Create a class name alias in the


// beginning of file.
use Zend\Version\Version;

4
5
6

// The "about" action


public function aboutAction() {

7
8
9
10
11
12
13

// Get current ZF version


$zendFrameworkVer = Version::VERSION;
// Fetch the latest available version of ZF
$latestVer = Version::getLatest();
// Test if newer version is available
$isNewerVerAvailable = Version::compareVersion($latestVer);

14
15
16
17
18
19
20
21
22

// Return variables to view script with the help of


// ViewObject variable container
return new ViewModel(array(
'zendFrameworkVer' => $zendFrameworkVer,
'isNewerVerAvailable' => $isNewerVerAvailable,
'latestVer' => $latestVer
));
}

The Zend\Version component is a small auxiliary component, which can be used by you if you
need to check the version of Zend Framework. To do that, you use the Version class.
In line 9, we use the VERSION constant defined in the Version class, which is the literal
representation of the version of Zend Framework currently installed in our machine (at the
moment of writing this text, this constant is equal to 2.2.1).
In line 11, we use the getLatest() static method to load the latest available version of ZF from
the Internet (note, that this requires the Internet connection and may take a few seconds to
complete).
In line 13, we use the compareVersion() static method to compare our current version with the
latest one (the comparison result will be -1 if our current version is newer, 0 if both equal, or 1 if
ours is older).
In lines 17-21, we pass the variables weve created to the constructor of the ViewModel object as
an associative array. The array keys define the names of the variables which on return will be
accessible to view script.
The ViewModel class provides several methods that you can additionally use to set variables to
ViewModel and and retrieve variables from it. The table 4.5 provides the methods summary:

82

Model-View-Controller

Table 4.5. Methods of the ViewModel class

Method name

Description

getVariable($name, $default)

Returns a variable by name (or default value if the


variable
does not exist).

setVariable($name, $value)

Sets a variable.

setVariables($variables, $overwrite)

Sets a group of variables, optionally overwriting the


existing ones.

getVariables()

Returns all variables as an array.

clearVariables()

Removes all variables.

4.5 Controller Registration


All controller classes belonging to a module should be registered in the module.config.php
configuration file as follows:
1
2
3

<?php
return array(
// ...

4
5
6
7
8
9
10
11

'controllers' => array(


'invokables' => array(
'Application\Controller\Index' =>
'Application\Controller\IndexController'
// Put other controllers registration here
),
),

12
13
14

// ...
);

In line 5, we have the the controllers key, which contains the invokables subkey. You should
register your controllers here. To register a controller class, you add the line in form of keyvalue
pair. The key should be the unique ID of the controller, like Application\Controller\Index, and
value should be the fully qualified class name of the controller, like Application\Controller\IndexController.
By registering your controller under the invokables subkey, you tell Zend Framework
that it can invoke the controller by instantiating it with the new operator. This is the
most simple way of instantiating the controller. As an alternative, you can register a
factory to create the controller instance, in that case you would register your controller
under the factories subkey.

83

Model-View-Controller

4.6 When to Create a New Controller?


When your site grows in size, you should create new controller classes instead of putting all
actions to IndexController. The Index controller is used for defining the actions which work
for your entire site. It is recommended to create new controller class for each model (or for most
important ones) of your business logic domain.
For example, you can create UserController to manage users of your site. This controller would
have the default index action for displaying the page with all users, add action for adding a
new user, edit action for editing users profile and delete action for deleting the user.
By analogy, you would create PurchaseController and its actions to manage the purchases
of your products and implementing the shopping cart, DownloadController and its actions to
manage file downloads for the site, etc.

4.7 Controller Plugins


A controller plugin is a class which extends the functionality of all controllers in some way.
Without plugins, to extend the functionality of all controllers, you would have to
create a custom base class, say BaseController, and derive other controllers from that
base class. This way is used in some PHP frameworks, but not in Zend Framework
2. From ZF2 creators point of view, plugins are better solution, because they use
class composition , which provides better flexibility comparing to class inheritance.
You register your plugin controller and it automatically becomes accessible from all
controllers of your app (AbstactActionController base class uses PHPs __call()
magic method to proxy calls to registered controller plugins).

There are several standard controller plugins available out of the box (table 4.6), and weve
already used one of them (the Params plugin) in one of our previous examples.
Table 4.6. Standard Controller Plugins

Standard Plugin Class

Description

Params

Allows to retrieve variables from HTTP request,


including GET and POST variables.

Url

Allows to generate absolute or relative URL addresses


from inside controllers.

Layout

Gives access to layout view model for passing data to


layout template.

Identity

Returns the identity of the user who has logged into the
web site.

Composition is a relationship between two classes that is best described as a has-a and whole/part relationship. The owner class contains
a reference to another class (plugin). The owner is responsible for the lifetime of the object it holds.

84

Model-View-Controller

Table 4.6. Standard Controller Plugins

Standard Plugin Class

Description

FlashMessenger

Allows to define flash messages which are stored in


session and can be displayed on a different web page.

Redirect

Allows to redirect the request to another controllers


action method.

PostRedirectGet

Redirects the POST request, converting all POST variables


to GET ones.

FilePostRedirectGet

Redirects the POST request, preserving uploaded files.

Inside of the controllers action method, you access a plugin in the following way:
1
2

// Access Url plguin


$urlPlguin = $this->url();

3
4
5

// Access Layout plugin


$layoutPlugin = $this->layout();

6
7
8

// Access Redirect plugin


$redirectPlugin = $this->redirect();

4.7.1 Writing Own Controller Plugin


In your web sites, you will definitely need to create custom controller plugins. For example,
assume you need that all your controller classes to be able to check whether a site user is allowed
to access certain controller action. This can be implemented with the AccessPlugin class.
The controller plugin should be derived from the AbstractPlugin class. Plugins typically live in
their own namespace Plugin, which is nested in Controller namespace:
<?php
namespace Application\Controller\Plugin;
use \Zend\Mvc\Controller\Plugin\AbstractPlugin;
// Plugin class
class AccessPlugin extends AbstractPlugin {
// This method checks whether user is allowed
// to visit the page
public function checkAccess($actionName){
// ...
}
}

Model-View-Controller

85

To let Zend Framework 2 know about your plugin, you need to register it in your module.config.php file under the controller_plugins key. See below for example:
<?php
return array(
// ...
'controller_plugins' => array(
'invokables' => array(
'Access' => 'Application\Controller\Plugin\AccessPlugin',
)
),
// ...
);

After that, youll be able to access your custom plugin from all of your controllers actions in
this way:
// Check if site user is allowed to visit the "index" page
$isAllowed = $this->access()->checkAccess('index');

4.8 Views
Views belong to the presentation layer of the web application, because their goal is to produce
HTML output returned by the web server to site visitors.
In Zend Framework 2, you implement a view as a template file, which is a file having .phtml
extension (phtml stands for PHP+HTML). View templates have such a name because they
usually contain HTML code mixed with PHP code snippets used for rendering the web pages.
Views typically live inside of the view subdirectory of the module (see figure 4.5):

86

Model-View-Controller

Figure 4.5. View directory

Why are view template files not stored under modules source directory?
View templates (.phtml files) are not stored under modules src/ directory, because
they are not usual PHP classes and do not need to be resolved by a PHP class
autoloading feature. View templates are resolved by the special ZF2 class called view
resolver, and for this reason, view templates are stored under the modules view
directory.

View templates can have different behaviors, based on variables you pass to them from the
controllers action method. Data are passed to view templates with the help of a ViewModel
variable container.
For example, lets implement the view template for the aboutAction() of our Index controller.
The About page will display the title, some information about our Hello World application, and
the information about the current Zend Framework version.
To create the view template file, in your NetBeans window, navigate to view/application/index
directory (see figure 4.6), and right click on the index directory name. From the context menu
that appears, select the New->PHP File menu item.
In the New PHP File dialog that appears (figure 4.7), enter the name about.phtml and click the
Finish button.
The about.phtml view template file will be created and displayed in the right pane of NetBeans
window. In that file, enter the following:

87

Model-View-Controller
1

<h1>About</h1>

2
3

<p>

The Hello World application.


</p>

5
6
7

<p>

Your Zend Framework version is


<?php echo $this->zendFrameworkVer; ?>
</p>

9
10
11
12

<?php if($this->isNewerVerAvailable): ?>

13
14

<p>

15

Your Zend Framework version is outdated. The latest available


version is <?php echo $this->latestVer; ?>.
</p>

16
17
18
19

<?php endif; ?>

Figure 4.6. Context Menu

As you can see, the view template is a usual HTML page with several PHP code fragments. A view
script just renders the data you pass to it with a ViewModel variable container. For example, in

88

Model-View-Controller

line 9 we get the value of $zendFrameworkVer variable and print it with the echo PHP statement.
In your view script, you can also use simple flow control operations (like if, foreach or switch)
to make the appearance of the page different depending on variables value. For example, in line
12 we have the if statement testing if the new Zend Framework version is available, and if yes,
the Your Zend Framework version is outdated text paragraph is displayed.
Now lets look at how the page looks like in the web browser. Type http://localhost/application/index/about
URL in your browsers navigation bar. The About page should appear (see figure 4.8):

Figure 4.7. Context Menu

89

Model-View-Controller

Figure 4.8. About Page

In general, the PHP code you use inside of views must be as simple as possible. Views
typically do not modify the data you pass from controller. For example, a view can use
the model you pass to it to walk through database table rows and render the items to
an HTML page, but it should never create database tables or modify them itself.

4.9 View Helpers


A view helper is typically a (relatively) simple PHP class whose goal is to render some part of a
view. You can invoke view helpers from any view template. With view helpers, you can create
reusable widgets (like menus, navigation bars, etc.) for your web pages.
View helpers are analogous to controller plugins: the controller plugins allow to
extend the functionality of controllers, and view helpers allow to extend the
functionality of view templates.

ZF2 provides many standard view helpers out of the box. In the table 4.7, some of them are
presented with a brief description:

90

Model-View-Controller

Table 4.7. Standard View Helpers

Standard Plugin Class

Description

BasePath

Allows to retrive the base path to the web application,


which is the absolute path to APP_DIR.

Url

Allows to generate absolute or relative URL addresses


from inside view templates.

ServerUrl

Retrieves the current requests URL.

Doctype

Helper for setting and retrieving the doctype HTML element


of the web page.

PageTitle

Helper for setting the title HTML element


of the web page.

HtmlList

Helper for generating ordered and unordered HTML lists.

ViewModel

Helper for storing and retrieving the view model

Layout

Retrieves the layout template view.

Partial

Allows to render a partial view template.

InlineScript

Helper for setting and retrieving script elements for


inclusion in HTML body section.

Identity

View helper to retrive the authenticated users identity.

FlashMessenger

Allows to retrieve the flash messages stored in


session.

To demonstrate the usage of a view helper, below we will show how to set a title for a web
page. Typically, it is required to give a different title per each web page. You can do this with
the HeadTitle view helper. For example, you can set the title for the About page by adding the
following PHP code in the beginning of the about.phtml view template:
<?php
$this->headTitle('About');
?>

In the code above, we call the HeadTitle view helper and pass it the page title string (About) as
the argument. The HeadTitle view helper internally sets the text for the <title> HTML element
of your web page. Then, if you open the About page in your web browser, the page title will look
like About - Zend Skeleton Application (see the figure below for an example):

91

Model-View-Controller

Figure 4.9. Setting page title for the About page

We will discuss the view helpers in more details and provide more usage examples in
Chapter 6.

4.10 View Template Names & View Resolver


When you return data with the ViewModel variable container from your controllers action
method, Zend Framework somehow knows the name of the corresponding view template file and
its location. For example, for your IndexControllers aboutAction() method, ZF2 automatically
uses the about.phtml view template.
It may be a surprise, but the ViewModel partially contributes into view template resolving.
Actually the ViewModel class is more than just a variable container. Additionally, it allows
to specify which view template should be used for page rendering. The summary of methods
provided for this purpose is shown in table 4.8.
Table 4.8. Methods of the ViewModel class for setting and retrieving the view template name

Method name

Description

setTemplate()
getTemplate()

Sets the view template name.


Returns the view template name.

To set the view template name, you use the setTemplate() method. The getTemplate() method
returns the view template name currently set for the view model.

Model-View-Controller

92

The following code example shows how you can call the setTemplate() method from your
IndexController class indexAction() method to force ZF2 to use the about.phtml view
template file for rendering the Home page, instead of the index.phtml file:
1
2

// Index action renders the Home page of your site.


public function indexAction() {

// Use a different view template for rendering the page.


$viewModel = new ViewModel();
$viewModel->setTemplate('application/index/about');
return $viewModel;

4
5
6
7
8

In the code above, we created a new instance of the ViewModel class as usual (line 5).
Then we called the setTemplate() method on the view model object (line 6) and passed the
name of the view template name as its argument. The view template name is actually a relative
path to the about.phtml file, minus file extension.
Finally, we returned the view model object from the action method (line 7).
However, calling the setTemplate() method in every action method is optional. If you dont
do that, ZF2 will determine the view template name automatically by concatenating the current
module name, controller name and action method name.
When Zend Framework has the template name, it only remains to determine the absolute path
to the corresponding .pthml file. This is also called the view template resolving. View templates
are resolved with the special Zend Frameworks class called the view resolver.
In ZF2, there are two view resolvers out of the box: TemplatePathStack and TemplateMapResolver.
Both resolvers take a view template name as input, and return path to view template file as
output. The template name is composed of controller name followed by template name, like
application/index/about, application/index/index, layout/layout and so on.
The template map resolver uses a PHP nested array to determine path to view template
file by its name. This way is fast, but you have to maintain some template map array and
update it each time you add a new view script.
The template path stack resolver assumes that the view template name can be mapped
to directory structure. For example, index/about template name maps to APP_DIR/module/Application/view/application/index/about.phtml. This way is simpler, because you
dont have to maintain any maps.
View resolver settings are stored inside of your module.php.config file under the view_manager
key:

Model-View-Controller
1
2
3

93

<?php
return array(
//...

4
5
6

'view_manager' => array(


//...

'template_map' => array(


'layout/layout' =>
__DIR__ . '/../view/layout/layout.phtml',
'application/index/index' =>
__DIR__ . '/../view/application/index/index.phtml',
'error/404' => __DIR__ . '/../view/error/404.phtml',
'error/index'=> __DIR__ . '/../view/error/index.phtml',
),
'template_path_stack' => array(
__DIR__ . '/../view',
),

8
9
10
11
12
13
14
15
16
17
18
19
20

),
);

You can see that template map resolvers settings are stored under the template_map key. By
default, there are several standard view templates, which are resolved this way: the index page
template, the layout template (we will talk about it in Chapter 6) and error templates (we will talk
about them a little bit later). These standard pages are served with this type of resolver, because
it is fast.
The template path stack resolvers settings are stored under the template_path_stack key. You
can see that this resolver looks for your view scripts under the view directory of your module.
Thats why we could just put about.phtml file under that directory, and ZF will automatically
find the template.
The template map resolver and template path stack resolver work in pair. First, the fast template
map resolver tries to find the template view in its array map, and if the page is not found, the
template path stack resolver is executed.

4.11 Disabling the View Rendering


Sometimes, you would need to disable the default view rendering. To do that, just return the
Response object from the the controllers action.
For example, lets create a DownloadController class, and add the file action, which would
allow site users to download files from your web site. This action does not need a corresponding
file.phtml view template, because it dumps file contents to PHP standard output stream and exits.
Add the DownloadController.php file to Controller directory of Application module, then put the
following code into the file:

Model-View-Controller
1
2

<?php
namespace Application\Controller;

3
4
5

use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;

6
7
8
9
10

/**
* This is the controller class for managing file downloads.
*/
class DownloadController extends AbstractActionController {

11
12
13
14
15
16
17
18
19

/**
* This is the 'file' action that is invoked
* when a user wants to download the given file.
*/
public function fileAction()
{
// Get the file name from GET variable
$fileName = $this->params()->fromQuery('name', '');

20
21
22
23

// Take some precautions to make file name secure


str_replace("/", "", $fileName); // Remove slashes
str_replace("\\", "", $fileName); // Remove back-slashes

24
25
26
27
28
29
30
31

// Try to open file


$path = './data/download/' . $fileName;
if (!is_readable($path)) {
// Set 404 Not Found status code
$this->getResponse()->setStatusCode(404);
return;
}

32
33
34

// Get file size in bytes


$fileSize = filesize($path);

35
36
37
38
39
40
41
42
43
44
45
46

// Write HTTP headers


$response = $this->getResponse();
$headers = $response->getHeaders();
$headers->addHeaderLine(
"Content-type: application/octet-stream");
$headers->addHeaderLine(
"Content-Disposition: attachment; filename=\"" .
$fileName . "\"");
$headers->addHeaderLine("Content-length: $fileSize");
$headers->addHeaderLine("Cache-control: private");

94

Model-View-Controller

95

// Write file content


$fileContent = file_get_contents($path);
if($fileContent!=false) {
$response->setContent($fileContent);
} else {
// Set 500 Server Error status code
$this->getResponse()->setStatusCode(500);
return;
}

47
48
49
50
51
52
53
54
55
56

// Return Response to avoid default view rendering


return $this->getResponse();

57
58

59
60

The action method takes the name parameter from URLs query part (line 19), removes slashes
from file name (lines 22-23), adds HTTP headers to Response object (lines 39-45) and file contents
(lines 48-55). Finally, it returns the Response object to disable the default view rendering.
Register the DownloadController class by adding the following line to your module.config.php
file:
<?php
return array(
// ...
'controllers' => array(
'invokables' => array(
// ...
'Application\Controller\Download' =>
'Application\Controller\DownloadController'
),
),
// ...
);

To see how the file download works, create APP_DIR/data/download directory and put some text
file named sample.txt in it. Then open your web browser and type the URL http://localhost/application/downloa
in your browsers navigation bar and press the Enter key. The browser will download the
sample.txt file and offer you to save it to some location.

4.12 Error Pages


When a page could not be found or some other error happens inside of your web application,
a standard error page is displayed. The appearance of the error page is controlled by the error
templates. There are two error templates: error/404 which is used for 404 Page Not Found

96

Model-View-Controller

error (shown in figure 4.10), and error/index which is displayed when an unhandled exception
is thrown somewhere inside of the application.

Figure 4.10. 404 Error Page

The module.config.php file contains several parameters under the view_manager key, which you
can use to configure the appearance of your error templates:
1
2
3

<?php
return array(
//...

4
5
6
7
8
9
10
11
12
13
14
15
16
17

'view_manager' => array(


'display_not_found_reason' => true,
'display_exceptions'
=> true,
//...
'not_found_template'
=> 'error/404',
'exception_template'
=> 'error/index',
'template_map' => array(
//...
'error/404' => __DIR__ . '/../view/error/404.phtml',
'error/index'=> __DIR__ . '/../view/error/index.phtml',
),
'template_path_stack' => array(
__DIR__ . '/../view',

Model-View-Controller

),

18
19
20

97

),
);

The display_not_found_reason parameter controls whether to display the detailed information about the Page not Found error.
The display_exceptions parameter defines whether to display information about an
unhandled exception and its stack trace.
The not_found_template defines the template name for the 404 error.
The exception_template specifies the template name for the unhandled exception error.

You typically set the display_not_found_reason and display_exceptions parameters to


false in production systems, because you dont want site visitors see the details about
errors in your site. However, you will still be able to retrieve the detailed information
from Apaches error.log file.

4.13 Models
A model is a PHP class which contains the business logic of your application. The business logic
is the core of your web site which implements the goal of site operation. For example, if you
implement an E-shop web site, you will have models implementing the product catalog and the
shopping cart.
In general, the term model means a simplified representation of a real-life object or phenomenon.
Simplified because the real-life object has infinite amount of properties. For example, a reallife person who visits your site consists of billions of atoms, and you cannot describe them all.
Instead, you take several properties of the object, which are the most important for your system
and ignore all others. For example, the most important properties of the site visitor (from web
site architects point of view) are first name, last name, country, city, ZIP code and street address.
Models can have some behavior. For example, a mailer model may send E-mail messages, the
currency converter model may be able to convert money and so on.
With ZF2, you represent models as usual PHP classes. Properties are implemented as class fields,
and the behaviors are implemented as class methods.

4.14 Model Types


In Zend Framework 2, there is no single Model directory for storing the model classes, as you
could assume. Instead, by convention, models are further subdivided into the following types,
and each type is stored in its own subdirectory (see table 4.9):

98

Model-View-Controller

Table 4.9. Model Types and their Location

Model Type

Directory

Entities

APP_DIR/module/Application/src/Application/Entity

Value Objects

APP_DIR/module/Application/src/Application/ValueObject

Services

APP_DIR/module/Application/src/Application/Service

Repositories

APP_DIR/module/Application/src/Application/Repository

Factories

APP_DIR/module/Application/src/Application/Factory

Separation of models into different types make it easier to design your business logic
domain. This is also called the Domain Driven Design (or shortly, DDD). The person
who proposed DDD was Martin Fowler in his famous book called Patterns of Enterprise
Application Architecture.

4.14.1 Entities
Entities always have some identifier property, so you can uniquely identify the object. For
example, a User entity always has a unique login property, and you can identify the user by
that attribute. You can change some other attributes of the entity, like firstName, or address,
but its identifier never changes. Entity models are usually stored in a database, in a file system
or in any other storage.
Below, you can find an example a User entity, which represents a site visitor:
1
2

// The User entity represents a site visitor


class User {

3
4
5
6
7
8
9
10
11
12

// Properties
private $login;
private $title;
private $firstName;
private $lastName;
private $country;
private $city;
private $zipCode;
private $address;

//
//
//
//
//
//
//
//

e.g.
e.g.
e.g.
e.g.
e.g.
e.g.
e.g.
e.g.

13
14
15
16
17

// Behaviors
public getLogin() {
return $this->login;
}
http://martinfowler.com/books/eaa.html

"admin"
"Mr."
"John"
"Doe"
"USA"
"Paris"
"10543"
"Jackson rd."

Model-View-Controller

99

18

public setLogin($login) {
$this->login = $login;
}

19
20
21
22

//...

23
24

In lines 5-10, we define User models properties. The best practice is to define the properties using
the private access type, and make them available to the caller through getter and setter public
methods (like getLogin() and setLogin(), etc).
Models behavior methods are not limited by getters and setters. You can create
other methods which manipulate with models data. For example, you can define the
getFullName() convenience method, which would return the users full name, like Mr.
John Doe.

4.14.2 Value Objects


Value objects are a kind of model for which the identity is not as important as for entities. A
value object is usually a small class identified by all of its attributes. It does not have an identifier
attribute. Value objects typically have getter methods, but do not have setters (value objects are
immutable).
For example, a model wrapping a money amount can be treated as a value object:
1

class MoneyAmount {

2
3
4
5

// Properties
private $currency;
private $amount;

6
7
8
9
10
11

// Constructor
public function __construct($amount, $currency='USD') {
$this->amount = $amount;
$this->currency = $currency;
}

12
13
14
15
16

// Gets the currency code


public function getCurrency() {
return $this->currency;
}

17
18
19

// Gets the money amount


public function getAmount() {

Model-View-Controller

100

return $this->amount;

20

21
22

// Converts the money amount into new currency


public static function convert($newCurrency) {
// Use currency exchange rates algorithm to determine the
// new amount.
if($this->currency=='USD' && $newCurrency=='EUR') {
$newAmount = 1.1*$this->amount;
return new MoneyAmount($newAmount, $newCurrency);
} else {
throw new Exception('Unknown currency code');
}
}

23
24
25
26
27
28
29
30
31
32
33
34

In lines 4-5 we define two properties: currency and amount. The model has no identifier property,
instead it is identified by all properties as a whole: if you change either the currency or amount,
you would have a different money amount object.
In lines 8-10 we define the constructor method, which initializes the properties. In lines 14-21,
we define getter methods for models properties. Note that we do not have setter methods (the
model is immutable).
In lines 24-33 we have the static method convert(), which is designed to convert money amount
to another currency using some simple currency conversion algorithm. Instead of modifying the
money amount properties, it constructs and returns a new MoneyAmount object.
The real life is much more difficult than we described above, and you should be
flexible enough to adapt your entities and value objects to real conditions. In some
situations you will have a value object which is mutable (have setter methods), and
sometimes, you will have only getter methods for some of entity properties. Do not
dwell on the idealistic recommendations provided in this chapter, instead adapt your
models as your intuition advices to you.

4.14.3 Services
Service models usually encapsulate some business logic functionality. Services typically do not
have state (internal properties), instead they manipulate other entity models and value objects.
Services usually have easily recognizable names ending with er suffix, like FileUploader or
UserManager.
Below, an example of Mailer service is presented. It has the sendMail() method which takes an
EmailMessage value object and sends an E-mail message using standard PHP mail() function:

Model-View-Controller

// The Email message value object


class EmailMessage {
private $recipient;
private $subject;
private $text;
// Constructor
public function __construct($recipient, $subject, $text) {
$this->recipient = $recipient;
$this->subject = $subject;
$this->text = $text;
}
// Getters
public function getRecipient() {
return $this->recipient;
}
public function getSubject() {
return $this->subject;
}
public function getText() {
return $this->text;
}
}
// The Mailer service, which can send messages by E-mail
class Mailer {
public function sendMail($message) {
// Use PHP mail() function to send an E-mail
if(!mail($message->getRecipient(), $message->getSubject(),
$message()->getText()))
{
// Error sending message
return false
}
return true;
}
}

101

Model-View-Controller

102

4.14.4 Factories
Factories are usually being designed to instantiate other models. In the simplest cases you
can create an instance of a class without any factory, just by using the new operator, but
sometimes class creation logic might be very complex, and you encapsulate it inside of a factory
class. Factory classes typically have names ending with Factory suffix, like ResourceFactory,
FileFactory, etc.
For example, lets consider the case when you use a CAPTCHA image in a web form. The image
is a type of challenge-response test used to determine whether the user is a human or a robot.
There may be different CAPTCHA types: simple image captcha, reCAPTCHA and so on. Lets
create a fictitious CaptchaFactory class, whose only goal is to create different CAPTCHA images
based on the $type argument you pass to its createCaptcha() method.
// The CaptchaFactory class is used to create CAPTCHA
// image for a web form.
class CaptchaFactory {
// Creates a CAPTCHA object based on type parameter
public static function createCaptcha($type) {
if($type=='Image')
return new ImageCaptcha();
else if($type=='ReCaptcha')
return new ReCaptcha();
else
throw new Exception('Unknown captcha type');
}
}

4.14.5 Repositories
Repositories are specific models responsible for storing and retrieving entities. For example, a
UserRepository may represent a database table and provide methods for retrieving User entities.
You typically use repositories when storing entities in a database. With repositories, you can
encapsulate SQL query logic in the single place and easily maintain and test it.
We will learn about repositories in more details in Chapter 10, when talking about
Doctrine library.

http://www.google.com/recaptcha

Model-View-Controller

103

4.15 Determining the Correct Model Type


Isnt it confusing to have so many model types?
Well, yes and no. At first, it may be a little difficult to determine the correct model
type, but as soon you improve your skills, you will be able to do that intuitively. Just
remember that model types improve the structure of your domain models.

When writing your own application having specific model domain, you may be confused when
trying to decide to which model type your class belongs (whether it is an entity, value object,
repository, service or factory). Below, a simple algorithm is provided to make it easier for you to
determine the correct model type when writing your own application:
Your model class is definitely a Service
if you call it from your controller class
if it has no state (private attributes)
if you think the best name for it ends with er: suffix, like FileUploader or
VersionChecker

Your model class is an Entity:


if your model is stored in a database
if it has an ID attribute
if it has both getters and setters methods
Your model class is a ValueObject:
if changing any attribute would make the model completely different
if your model has getters, but not setters (immutable)
Your model is a Repository:
if it works with a database to store and retrieve entities
Your model is a Factory:
if it can create other objects and can do nothing else
If nothing above matches your model specification, then your model probably is either a service
or a value object. If your model does not have a state, put it to services, otherwise to value objects.
Hmm what if I just store all my models in a single Model directory?
Of course, you can, if you strongly wish. But, when you use Doctrine ORM library, you
will notice that it utilizes DDD principles as well, so using DDD makes your application
well-organized.

Model-View-Controller

104

4.16 Skinny Controllers, Fat Models, Simple Views


When developing a web site using Model-View-Controller pattern, there is a risk of misunderstanding the role of controllers, views and models. This results in making the controllers huge
and models small, which in turn makes it difficult to test and support your application. This
sections goal is to give you a general understanding of what code may be placed in a controller
class, what code may be placed in a view template, and what code may be placed in a model
class.

4.16.1 Skinny Controllers


The idea behind the term skinny controller is that typically, in your controller classes, you put
only the code that:

accesses user request data ($_GET, $_POST, $_FILES and other PHP variables);
(optionally) makes some basic preparations to the data;
instatiates the model class(es) (or gets service(s) registered in the ServiceManager);
passes the data to model(s) and retrieves the result returned by the model(s);
and finally returns the output data as a part of a ViewModel variable container.

A controller class should avoid:


containing complex business logic, which is better kept in model classes;
containing any HTML or any other presentational markup code. This is better be put in
view templates.
For an example of a skinny controller, look at the CurrencyConverterController class below.
This controller provides the convert action method whose goal is to convert an amount of
money from EUR to USD currency. The user passes the amount of money through the amount
GET variable.
1

class CurrencyConverterController extends AbstractActionController {

2
3

public function convertAction() {

4
5
6
7
8
9
10
11

// Get the money amount from GET


$amount = (float)$this->params()->fromQuery('amount', -1);
if($amount==-1) {
// Money amount is missing
$this->getResponse()->setStatusCode(404);
return;
}

12
13

// Create CurrencyConverter model

Model-View-Controller

105

$currencyConverter = new CurrencyConverter();


$convertedAmount = $currencyConverter->convertEURtoUSD($amount);

14
15
16

return new ViewModel(array(


'amount'=>$amount,
'convertedAmount'=>$convertedAmount
));

17
18
19
20

21
22

The controllers action method above does the following:


Takes the data passed by site user (line 6). This data is usually part of Request object and
can be retrieved using the controllers getRequest() method or Params controller plugin.
Performs the basic check on the data passed by user (line 7), and if the data is missing (or
invalid), sets an HTTP error code (line 9). Here, we only check if the variable amount
is present, because otherwise we cant pass the money amount to the model class. More
complex parameter checks should be performed inside of the model class.
Creates the CurrencyConverter model (line 14) and passes the money amount to the model
by calling its convertEURtoUSD() method. The method then returns the converted amount.
Constructs the ViewModel variable container and passes the resuting data to it (line
17). This variable container can be further accessed in the corresponding view template
responsible for data presentation.

4.16.2 Fat Models


Because you need to keep your controllers as thin as possible, most of the business logic of your
application should be put into model classes. In a properly designed Model-View-Controller
application, models look huge. A model class may contain the code which:
Performs complex data filtering and validation. Because the data that you retrieved in
controller is passed to your application from an outside world, in your model, you have to
take a lot of effort to verify the data and ensure the data will not break your system. This
results in a secure web site resistent to hacker attacks.
Performs data manipulation. Your models should manipulate the data: e.g. load the data
from database, save it to database and transform the data. Models are the right place for
storing database queries, file reading and writing functionality, and so on.
In a model class you are not recommended to:
Access the data from the HTTP request, $_GET, $_POST and other PHP variables. The
controllers work is to extract that data and pass it to models input.
Produce HTML or other code specific to presentation. The presentational code may vary
depending on the user request, and it is better to put it in a view template.

Model-View-Controller

106

If you follow this principles, you will encounter that your models are easy to test, because they
have clearly identified input and output. You can write a unit test which passes some test data
to input end of the model, retrieves the output data and verifies that the data is correct.
If you are confused whether to put certain code in a controller or in a model, ask yourself: is this
an important logic that needs to be carefully tested? If the answer is yes, you should put the code
in a model.

4.16.3 Simple View Templates


Because most of the logic is stored in models, your view templates should be as simple as possible
to produce the presentation of the data passed through the variable container. In a view template,
you may:
Keep static HTML markup code.
Retrieve the data from a variable container and echo them to PHP output stream.
If a controller passed a certain model through a variable container, poll the model for data
(e.g. you can retrieve table rows from a database table and render them).
Contain simple PHP flow control operations, like if, foreach, switch and so on. This
allows to vary the presentation depending on variables passed by the controller.
The view template is not recommended to:
Access data from the HTTP request and PHP variables.
Create models, manipulate them and modify the state of the application.
If you follow these principles, you will encounter that your views can easily be substituted
without modifying the business logic of your application. For example, you can easily change
the design of your web pages, or even introduce changable themes.

4.17 Summary
A Zend Framework 2 based web site is just a PHP program receiving an HTTP request from
the web server, and producing an HTTP response. The web application uses the Model-ViewController pattern to separate business logic from presentation. The goal of this is to allow for
code reusability and separation of concerns.
A controller is a mediator between the application, models and views: it gets input from HTTP
request and uses the model(s) and the corresponding view to produce the necessary HTTP
response. A controller is a usual PHP class containing action methods.
Views are simple HTML+PHP code snippets producing HTML output returned by the web server
to site visitors. You pass the data to view scripts through the ViewModel variable container.
A model is a PHP class which contains the business logic of your application. The business logic
is the core of your web site which implement the goal of site operation. Models can access
database, manipulate disk files, connect to external systems, manipulate other models and so on.

5 URL Routing
When a site user enters a URL in a web browser, the request is finally dispatched to controllers
action. In this chapter, we will learn about how ZF2-based application maps page URLs to
controllers and their actions. This mapping is accomplished with the help of routing. Routing
is implemented as a part of Zend\Mvc component.
ZF2 components covered in this chapter:
Component

Description

Zend\Mvc

Implements support of MVC and routing.

Zend\Barcode

Auxiliary component implementing barcodes.

5.1 URL Structure


To better understand routing, we first need to look at the URL structure. A typical URL from an
HTTP request consists of segments. The segments are URL parts delimited by slash characters
(/): there are scheme, host name, path and query segments.
For example, lets look at the URL http://site1.yourserver.com/path/to/page?query=Search
(figure 5.1).

Figure 5.1. Typical URL structure

This URL begins with a scheme segment (the scheme typically looks like http or https).
Then, the host name segment follows which is the domain name of your web server (like
site1.yourserver.com). Optional path segments follow the host name. So if you have the path
part /path/to/page then path, to, and page would each be a URL segment. Next, after
the question mark, the optional query part follows. It consists of one or several name=value
parameters separated from each other by an ampersand character (&).
Each segment in a URL uses special character encoding, which is named the URL encoding. This
encoding ensures that the URL contains only safe characters from the ASCII table. If a URL
contains unsafe characters, they are replaced with a percentage character (%) followed by two
hexadecimal digits (for example, the space character will be replaced by %20).
ASCII (American Standard Code for Information Interchange) is a character set which can be used to encode characters from the English
alphabet. It encodes 128 characters: digits, letters, punctuation marks and several control codes inherited from Teletype machines.

108

URL Routing

5.2 Route Types


Routing is a mechanism which allows to map HTTP request to the controller. With routing,
ZF2 knows which of the controllers action method to execute as the result of the request. For
example, you can map http://localhost/ URL to IndexController::indexAction() method or
http://localhost/about URL to IndexController::aboutAction() method.
A typical routing rule has the name, type and options. The name is used to uniquely identify
the rule. The type defines the name of the PHP class which implements the algorithm used for
comparing the URL string. The options is an array that includes the route string which should
be compared against the URL string, and several parameters called the defaults.
In general, the routing algorithm may use any data from HTTP request for matching the route.
However, typically, it takes only the URL string (or its substring) as input. The algorithm then
compares the URL with the route, and if the URL string matches the route, returns several
parameters, including the controllers name and action methods name, and possibly others.
These parameters may be either hard-coded in a configuration file or grabbed from the URL
string. If a certain parameter cannot be retrieved from the URL, its default value is returned.
There are several standard route types provided by Zend Framework 2 (shown in table 5.1). These
route types are implemented as classes living in the Zend\Mvc\Router\Http namespace.
Table 5.1. Route Types

Route Type

Description

Literal

Exact matching against a path part of a URL.

Segment

Matching against a path segment (or several segments) of a URL.

Regex

Matching the path part of a URL against a regular expression template.

Wildcard

Matching the path part of a URL against a key/value pattern.

Hostname

Matching the host name against some criteria.

Scheme

Matching URL scheme against some criteria.

Method

Matching an HTTP method (e.g. GET, POST, etc.) against some criteria.

Each route type in the table above (except the Method type) may be matched against a specific
segment (or several segments) of a URL. The Method route type is matched against the HTTP
method (either GET or POST) retrieved from HTTP request.
There is also the Query route type, which is declared deprecated and is not recommended to use. This route type is actually not needed, because you can retrieve query
parameters from your URL with the Params controller plugin (see the Retrieving Data
from HTTP Request section in Chapter 4).

109

URL Routing

5.3 Combining Route Types


Routes may be combined with the help of aggregate route types (shown in table 5.2). The
compound route types allow to define arbitrarily complex URL mapping rules.
Table 5.2. Aggregate Route Types

Route Type

Description

SimpleRouteStack

Aggregates different route types in a list with priorities.

TreeRouteStack

Aggregates different route types in a tree-like structure.

Part

Aggregates different route types in a subtree.

Chain

Aggregates different route types in a chain (degenerated subtree).

The TreeRouteStack and SimpleRouteStack are used as the top-level route types. The
SimpleRouteStack allows to organize different routing rules in a priority list. The TreeRouteStack
allows to nest different routing rules, forming a tree.
Figure 5.2 shows the route class inheritance diagram.

Figure 5.2. Route class inheritance diagram

As you can see from the image, all route classes are inherited from RouteInterface interface (we
will learn this interface in details in the Writing Own Route Type section later in this chapter).
The SimpleRouteStack is a parent class for TreeRouteStack class, which inherits the behavior
of the simple route stack (allows to organize routes in priority list) and extends it (allows to
organize routes in subtrees). The Part and Chain classes are derived from TreeRouteStack class
and are used internally by the TreeRouteStack for building subtrees and chains of child routes.

110

URL Routing

You may notice that the SimpleRouteStack class lives in the Zend\Mvc\Router namespace, while other route classes live in its sub-namespace Zend\Mvc\Router\Http.
This is because routing is also used for mapping shell commands to controllers
in console applications (see Chapter 8). Thus, console route classes will live in
Zend\Mvc\Router\Console, while the SimpleRouteStack compound route type will be
used for both HTTP routing and console routing.

5.3.1 Simple Route Stack


The SimpleRouteStack allows to combine different route types in a priority list. For an example
of such a list, look at the route stack in the left part of figure 5.3. The example list contains several
Literal routes and several Segment routes.
When matching against the HTTP request, the SimpleRouteStack walks through the list of
routes and tries to match each route in turn. Each route in the list has a priority; the routes
with the higher priority are visited first. The lookup is finished once some route matches the
HTTP request. If none of the routes match, the not found error is raised.

Figure 5.3. An example of Simple Route Stack (left) and Tree Route Stack (right)

5.3.2 Tree Route Stack


The TreeRouteStack class extends the SimpleRouteStack class, which means it can organize
the routes in a priority list, plus it provides an ability to nest routes in subtrees and chains. An
example tree route stack is presented in the right part of figure 5.3. The list contains of one
Literal route, a chain of Literal and Segment routes, and a subtree consisting of two branches:

111

URL Routing

a branch containing a single Segment route, and a branch consisting of Scheme, Hostname and
Segment routes.
The tree route stack performs request matching in the following way. It walks through its priority
list items (denoted by dashed lines in figure 5.3), starting from high-priority routes. If a certain
item is a Chain route or a Part route, it processes such a nested route from its parent route to
children. If the parent route matches, the children (denoted with solid lines) are analyzed then.
The nested route is considered matching if at least one route matches in each tree (or chain) level.
Each route in a tree (or chain) consumes a part of the URL (figure 5.4). The parent route is
matched against the first segment (or several segments) of the URL, its child is matched again
the next segment (or several segments), and so on, until the end of the URL string is reached.

Figure 5.4. An example of nested route matching

5.4 Routing Configuration


You typically do not create the route stack (or tree) yourself, instead you provide the instructions
for ZF2 on how to do that. The routing configuration for a module is stored in module.config.php
configuration file:
1
2
3
4
5
6
7
8
9
10
11

<?php
return array(
//...
'router' => array(
'router_class' => 'Zend\Mvc\Router\Http\TreeRouteStack',
'routes' => array(
// Register your routing rules here...
),
'default_params' => array(
// Specify default parameters here for all routes here ...
)

112

URL Routing
12
13

),
);

Above, in line 4 we have the router key, under which there is the routes subkey (line 6), containing
the routing rules.
You can specify which top-level route class to use (either TreeRouteStack or SimpleRouteStack)
with the router_class parameter (line 5). If this parameter is not present, the TreeRouteStack
is used by default.
You can use the optional default_params key (line 9) to define the default parameters for all
routes at once. However, you typically do not use this key and define the defaults on a per-route
basis.

5.4.1 Configuration for Simple Routes


Configuration for each routing rule under the routes subkey may have the following format:
'<route_name>' => array(
'type' => '<route_class>',
'priority' => <priority>,
'options' => array(
'route' => '<route>',
'defaults' => array(
//...
),
),
),

Above, the <route_name> placeholder should be the name of the route. A route name must be in
lower case, like home or about. The type key specifies the route class name. It can be either
the full class name, like ZendMvcRouterHttpLiteral, or a short alias, like Literal.
The optional priority key allows to define the priority (which should be an integer number)
of the route in the priority list (routes with higher priority will be visited first). If you omit the
priority key, the routes will be visited in the LIFO order.
Routes having equal priority will be visited in the LIFO order. Thus, for the best
performance, you should register routes that will match most often in the last turn,
and least common routes should be registered first.

The options key defines the array of routes options. We will discuss the options in the following
sections of this chapter.

5.4.2 Configuration for Nested Routes


To organize routes in a subtree, you add the child_routes key to the route definition, and add
your child routes under that key, like below:
LIFO (stands for Last In, First Out) is used to organize items in a stack, where the topmost item, which is added last, is taken out first.

113

URL Routing

'<route_name>' => array(


'type' => '<route_class>',
'priority' => <priority>,
'options' => array(
//...
),
'child_routes' => array(
// Add child routes here.
// ...
)
),

If you need to organize the routes in a chain (degenerated subtree), you add the chain_routes
key to your route configuration:
'<route_name>' => array(
'type' => '<route_class>',
'priority' => <priority>,
'options' => array(
//...
),
'chain_routes' => array(
// Add chained routes here.
// ...
)
),

Looking at the two examples above, you wont see the explicit usage of Part and Chain
route types, because (for your convenience) they are used by the ZF2 automatically
when it encounters the child_routes and chain_routes keys in your routing configuration.

5.4.3 Default Routing Configuration in Zend Skeleton


Application
Now that you know how to configure routes and organize them in a compound structures, lets
look at the real life example. In a fresh Zend Skeleton Application, the routing configuration
looks like below:

URL Routing
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

<?php
return array(
'router' => array(
'routes' => array(
'home' => array(
'type' => 'Zend\Mvc\Router\Http\Literal',
'options' => array(
'route'
=> '/',
'defaults' => array(
'controller' => 'Application\Controller\Index',
'action'
=> 'index',
),
),
),
// The following is a route to simplify getting started creating
// new controllers and actions without needing to create a new
// module. Simply drop new controllers in, and you can access them
// using the path /application/:controller/:action
'application' => array(
'type'
=> 'Literal',
'options' => array(
'route'
=> '/application',
'defaults' => array(
'__NAMESPACE__' => 'Application\Controller',
'controller'
=> 'Index',
'action'
=> 'index',
),
),
'may_terminate' => true,
'child_routes' => array(
'default' => array(
'type'
=> 'Segment',
'options' => array(
'route'
=> '/[:controller[/:action]]',
'constraints' => array(
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action'
=> '[a-zA-Z][a-zA-Z0-9_-]*',
),
'defaults' => array(
),
),
),
),
),
),
),

114

115

URL Routing
47
48
49

//...
);

This configuration corresponds to the tree route stack shown in figure 5.5:

Figure 5.5. Default route stack in the Skeleton Application

In the configuration presented above, we have two routing rules listed in turn: first we have the
home route (line 5) and then we have the compound route (line 19), consisting of two routes.
The parent route is named application. You can see that under the child_routes subkey it has
the child rule named default. This compound route also has a name: it is the concatenation of
all names of routes it consists of (application/default).
The may_terminate parameter (line 29) of the parent route defines whether the child route must
always match or the child route is optional. If the may_terminate parameter is true and the
end of the URL is reached, the child route is ignored and the route is treated as matching. If
the may_terminate parameter is false and the last segment of the URL is reached, the route is
treated as non-matching.
In the next sections, we will provide some examples on how to use the route types in your web
site.

5.5 Literal Route Type


With Literal route type, the route match is achieved only when you have the exact literal match
of the route string against the URL path. You typically use the Literal type for URLs which should
be short and memorable, like /about or /news.
Below, the definition of the route named home is presented. The home route is usually
mapped to the index action of the IndexController and points to the Home page of your
site:

URL Routing
1
2
3
4
5
6
7
8
9
10

116

'home' => array(


'type' => 'Zend\Mvc\Router\Http\Literal',
'options' => array(
'route'
=> '/',
'defaults' => array(
'controller' => 'Application\Controller\Index',
'action'
=> 'index',
),
),
),

Line 2 of this example says that the routes type is Literal. The actual route matching algorithm
is implemented in the Zend\Mvc\Router\Http\Literal class. You can either specify the full class
name or its short alias (Literal).
Line 4 defines the route string to match against the URL path (the forward slash / means the
empty URL part). Because we have the literal route type, the route match is achieved only when
you have the exact literal path match. For example, if you have the URL http://localhost/ or
http://localhost, it will match the / route string.
Lines 5-8 define the defaults, which are the parameters returned by the router if the route
matches. The controller and action parameters define the controller and controllers action
method which should be executed. You can also define other parameters here, if needed.
As another example of the Literal route type, lets add the /about route for the About page
weve created earlier in the Views section of Chapter 4. To create the route, add the following
lines right after the home rule definition inside of your module.config.php file:
'about' => array(
'type' => 'Literal',
'options' => array(
'route' => '/about',
'defaults' => array(
'controller' => 'Application\Controller\Index',
'action'
=> 'about',
),
),
),

If you now open the http://localhost/about URL in your web browser, you should see the About
page.

5.6 Segment Route Type


The Segment route type allows for matching the route string against one or several URL path
segments.

117

URL Routing

If you look at the module.php.config file, you can see the Segment route type is used
inside of the application/default route to make actions of your controllers automatically mapped to site URLs. You just add an action method to your controller, and
it becomes available by a URL like http://localhost/<module>/<controller>/<action>.
For example, you can see the About page of your site with the following URL:
http://localhost/application/index/about.

To demonstrate the creation of the Segment route type, lets implement a controller action
which will generate a simple barcode image. Barcodes are widely used in supermarkets for
optically recognizing goods in your shopping cart. The barcodes may be of different types and
have different labels. We will use the Segment route type to map the action to a URL like
http://localhost/barcode/<type>/<label>.
First, we define the barcode routing rule in the module.config.php file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14

'barcode' => array(


'type' => 'Segment',
'options' => array(
'route' => '/barcode[/:type/:label]]',
'constraints' => array(
'type' => '[a-zA-Z][a-zA-Z0-9_-]*',
'label' => '[a-zA-Z0-9_-]*'
),
'defaults' => array(
'controller' => 'Application\Controller\Index',
'action' => 'barcode',
),
),
),

Segments of the route string (line 4) may be constant or variable. You can define the variable
segments by using wildcards. We have three segments: barcode, :type and :label. The
barcode segment is constant, while the latter two are wildcards (wildcards name should start
with a colon).
You specify how a wildcard should look like inside of the constraints subkey (lines 5-8). We
define the regular expression [a-zA-Z][a-zA-Z0-9_-]* which constraints our :type wildcard
to begin with a letter and contain one or several letters, digits, underscores or minus characters.
The constraint for the :label wildcard is almost the same, but this segment can start with any
allowed character (either letter, digit, underscore or minus sign character).
Optional segments can be enclosed in square brackets. In our example, we have both the :type
and :label segments as optional.
In lines 9-12, we define the defaults, the parameters that will be returned by the router. The
controller and action defaults specify which controller and action method to execute on route
match.
Next, we add the barcodeAction() method into the IndexController class:

118

URL Routing
1
2

// Add name alias in the beginning of the file


use Zend\Barcode\Barcode;

3
4

// ...

5
6
7

// The "barcode" action


public function barcodeAction() {

// Get parameters from route.


$type = $this->params()->fromRoute('type', 'code39');
$label = $this->params()->fromRoute('label', 'HELLO-WORLD');

9
10
11
12

// Set barcode options.


$barcodeOptions = array('text' => $label);
$rendererOptions = array();

13
14
15
16

// Create barcode object


$barcode = Barcode::factory($type, 'image',
$barcodeOptions, $rendererOptions);

17
18
19
20

// The line below will output barcode image to standard


// output stream.
$barcode->render();

21
22
23
24

// Return Response object to disable default view rendering.


return $this->getResponse();

25
26
27

In lines 10-11 we get the values of the type and label wildcards from route. We do that with the
help of Params controller plugins fromRoute() method. Analogous to fromQuery() method, it
takes two arguments: the variable name and its default value.
For generating the barcode image, we use the Zend\Barcode component. In line 14 we define the
label text for the barcode. In lines 18-19 we create the Barcode object with the factory method.
Finally, in line 23 we render the image file by dumping it to PHP output stream.
Zend\Barcode is an auxiliary component used for generation of various barcode

images. For additional information about this component, please refer to the corresponding section of Zend Framework reference manual.

In line 26 we return the Response object to suppress the default view rendering.
Now, enter the http://localhost/barcode URL into your browser to see the barcode image
(shown in figure 5.6):

119

URL Routing

Figure 5.6. An example barcode image

Please note that for barcode images to work, you need to have the GD extension of
the PHP engine installed and enabled. Please refer to Appendix A for instructions on
how to do that.

Because we have the wildcards in the route, you can pass the type and label parameters of the
barcode image in the URL. Below, several URL examples are provided (corresponding barcodes
are presented in figure 5.7):
a.
b.
c.
d.
e.
f.
g.
h.

http://localhost/barcode/code39/HELLO-WORLD
http://localhost/barcode/leitcode/12345
http://localhost/barcode/identcode/98765453212
http://localhost/barcode/postnet/123456
http://localhost/barcode/planet/1234567890123
http://localhost/barcode/upca/12345678901
http://localhost/barcode/code128/ABCDEF
http://localhost/barcode/ean2/12

Figure 5.7. Barcode types


PHP GD extension allows to create image files in different formats (like JPEG, PNG, GIF, etc.)

120

URL Routing

5.7 Regex Route Type


The regular expression route type (Regex) is useful if you have URLs which can be matched
against a regular expression.
For example, assume you want to create a simple documentation system for your web site. The
documentation would consist of static pages mapped to URLs like /doc/<page_name>.html.
By the term static page we refer to a page which mostly contains static HTML code
plus several PHP inline fragments. For such simple pages you do not need to create
separate controller actions. All static pages can be served by the single controller
action.

Lets implement the route which will serve the static pages of the site. Because static pages
are simple, you typically wont need to add per-page action methods to the controller. All pages
will be handled by the single action IndexController::docAction().
First, we add the Regex route named doc to the module.config.php file:
1
2
3
4
5
6
7
8
9
10
11

'doc' => array(


'type' => 'Zend\Mvc\Router\Http\Regex',
'options' => array(
'regex'
=> '/doc(?<page>\/[a-zA-Z0-9_\-]+)\.html',
'defaults' => array(
'controller' => 'Application\Controller\Index',
'action'
=> 'doc',
),
'spec'=>'/doc/%page%.html'
),
),

Line 2 defines the Regex type for the route. In line 4, we have the regular expression /doc(?<page>\/[a-zA-Z0-9_\-]+)\.html. It will match to URLs like /doc/contents.html, /docs/introduction.html and so
on. The expression contains the named capture page, which will be returned by the router on
match together with the default parameters.
Line 9 contains spec option, which is used for generating URLs by route (we will discuss
generating URLs by route later in this chapter).
Next, add the following action to IndexController class:

In PHP PCRE regular expressions, it is possible to name a sub-pattern using the syntax (?P<name>pattern). This sub-pattern will then be
indexed in the matches array by its name.

URL Routing
1

121

public function docAction() {

$pageTemplate = 'application/index/doc'.
$this->params()->fromRoute('page', 'documentation.phtml');

3
4
5

$filePath = __DIR__.'/../../../view/'.$pageTemplate.'.phtml';
if(!file_exists($filePath) || !is_readable($filePath)) {
$this->getResponse()->setStatusCode(404);
return;
}

6
7
8
9
10
11

$viewModel = new ViewModel();


$viewModel->setTemplate($pageTemplate);

12
13
14

return $viewModel;

15
16

In lines 3-4 above, we retrieve the page parameter from route (remember the page named
capture from our regular expression?) and save it as the $pageTemplate variable. We will use the
$pageTemplate variable for determining the view template name to pass to the view resolver.
Then, in lines 6-10, we check if such a file name is present, and if not, return the 404 Not Found
status code, which will force ZF2 to display the error page. In line 12, we create the ViewModel
variable container, and in line 13 we explicitly set the view template name for rendering.
To see the documentation system in action, create a couple of static view template files: the
Table of Contents page (contents.phtml) and the Introduction page (introduction.phtml).
Create the doc subdirectory under the view/application/index directory of the Application
module and put the contents.phtml view template there:
1

<h1>Table of Contents</h1>

2
3
4
5
6
7
8
9
10

<ul>
<li>
<a href="<?php echo $this->url('doc',
array('page'=>'introduction')); ?>">
Introduction
</a>
</li>
</ul>

In the lines above, we provide the HTML code for the Table of Contents page header, and the
list which contains the single item named Introduction pointing to the Introduction static
page. The link URL is generated with the Url view helper (for more details on the Url helper, see
further sections in this chapter).
Then add the introduction.phtml page into the same doc directory:

122

URL Routing
1

<h1>Introduction</h1>

2
3

<p>Some introductory materials.</p>

In the lines above, we define the HTML markup for the simple Introduction page.
Now, if you open the http://localhost/doc/contents.html URL in your browser, you should see
a nice simple documentation system which you can extend and use in your site (figure 5.8):

Figure 5.8. Static Page

Clicking the Introduction link will direct you to the Introduction static page. You can also add
other pages to the doc directory to make them automatically available for site users through our
Regex route.
One disadvantage of such a documentation system is that it does not work well if
you place nested pages in subdirectories under the doc directory. The reason of this
limitation lies in the way the Regex route assembles URLs. You cant generate URLs
containing slash characters, as these unsafe characters will be automatically URLencoded. We will work-around this problem with our custom route type that we will
create at the end of this chapter.

5.8 Wildcard Route Type


The Wildcard route type is designed specially for URLs consisting of several key/value pairs.
These URLs usually look like http://localhost/key1/value1/key2/value2[/key3/value3 ]. The
URL may contain variable count of name/value pairs. Because the Wildcard route type may
consume all segments up to the end of the URL, it should not have child routes.

URL Routing

123

To demonstrate the usage of the Wildcard route type, lets assume we need to create an action
that would display blog post(s) published during some period of time. For this action, we would
like to use URLs looking like http://localhost/blog/year/2013/month/April/name/my-vacation.
First, lets add the blogAction() method to our IndexController class:
1
2
3
4
5

public function blogAction() {


// Get parameters from the route.
$year = $this->params()->fromRoute('year', null);
$month = $this->params()->fromRoute('month', null);
$name = $this->params()->fromRoute('name', null);

if($name!=null)
echo 'You requested to see the blog post named "'.$name.'"';
else
echo 'You requested to see all blog posts';

7
8
9
10
11

if($year!=null && $month!=null)


echo ' published in '.$month.' '.$year;
else if($year!=null)
echo ' published in '.$year;
else
echo ' published in the past';

12
13
14
15
16
17
18

// Suppress default view rendering.


return $this->getResponse();

19
20
21

In the code above, we get $year, $month and $name parameters from route as usual (lines 3 - 5).
Because the parameters are optional, we echo different text to the screen based on which
parameters are present. If the user provided the name of the blog post to show, we display its
name, otherwise we display all blog posts for the period. If the user provided the year and month,
we display the blog post(s) for this year and month; if the user provided only the year, we display
blog posts for the year. If the user didnt provide neither year nor month, we display all posts for
the period.
Next, define the routing configuration for this action. Add the following lines under your router
key:

URL Routing
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

124

'blog' => array(


'type'
=> 'Literal',
'options' => array(
'route'
=> '/blog',
'defaults' => array(
),
),
'may_terminate' => false,
'child_routes' => array(
'wildcard' => array(
'type' => 'Zend\Mvc\Router\Http\Wildcard',
'options' => array(
'key_value_delimiter' => '/',
'param_delimiter' => '/',
'defaults' => array(
'controller' => 'Application\Controller\Index',
'action'
=> 'blog',
),
),
),
),
),

The key_value_delimiter option defines the character which separates key/value pairs, typically you set this with slash (/) character, but you can use any other one. The param_delimiter
option defines the character which separates the key and value within the pair. Typically, this is
also the / character.
To see how the wildcard route works, type the following URL in your web browsers navigation bar: http://localhost/blog/year/2013/month/April/name/my-vacation. You should see
the following text in your browser: You requested to see the blog post named my-vacation
published in April 2013. If you enter the URL http://localhost/blog/year/2013, you should see
the following output: You requested to see all blog posts published in 2013.

5.9 Other Route Types


The Hostname, Scheme, and Method route types are used less commonly compared to the route
types mentioned previously.
The Hostname route type can be used, for example, if you develop a content management system
(CMS) engine, which should serve several web sites at once, each site using a different subdomain. In that case you will define the Hostname route as the parent, and nest child routes of
other types inside of it:
A Content Management System (CMS) is a web site allowing for collaborative creating, editing and publishing content (blogs, pages,
documents, videos etc.) using a centralized web interface. CMS systems make it possible for non-programmers to perform the web sites daily
tasks, like content publishing.

URL Routing
1
2
3
4
5
6
7
8
9
10
11
12
13
14

125

'routename' => array(


'type' => 'Zend\Mvc\Router\Http\Hostname',
'options' => array(
'route' => ':subdomain.yourserver.com',
'constraints' => array(
'subdomain' => '[a-zA-Z][a-zA-Z0-9_-]*'
),
'defaults' => array(
),
),
'child_routes'=>array(
//...
),
),

In the example above, in line 1 we define the route which has the Hostname type. The route
option (line 4) defines the domain name to match against. The :subdomain is a wildcard, which
can take different sub-domain values. The constraints key defines the regular expression this
sub-domain parameter must match. The Hostname route will differentiate your domains, so each
site will behave differently, depending on the value of the subdomain parameter returned:
// An example of an action that uses parameters returned by
// Hostname route.
public function someAction() {
// Get the 'subdomain' parameter from the route.
$subdomain = $this->params()->fromRoute('subdomain', null);
// Use different logic based on sub-domain.
//...
// Render the view template.
return new ViewModel();
}

The Scheme route type is useful if you need to handle HTTP and HTTPS protocols in different
ways.
The typical Scheme route configuration is presented below:

The HTTPS protocol is typically used for secure connections, like account page or shopping cart page. When you use HTTPS, the request
data is tunnelled through Secure Socket Layer (SSL) channel and not available to third parties.

URL Routing
1
2
3
4
5
6
7
8
9
10
11
12

126

'routename' => array(


'type' => 'Zend\Mvc\Router\Http\Scheme',
'options' => array(
'scheme' => 'https',
'defaults' => array(
'https' => true,
),
),
'child_routes'=>array(
//...
),
),

Above, we define the route of type Scheme. It takes the scheme option, which should be the
scheme to match against (like http or https). If the scheme in HTTP requests URL is exactly
the same as the scheme option, the route is considered matching. You can use the defaults key
to return some parameters on route match. In the example above, the https boolean parameter
will be returned.
The Method route type can be used if you need to direct GET and POST requests into different
controllers actions. Its typical configuration is presented below:
1
2
3
4
5
6
7
8
9
10
11

'routename' => array(


'type' => 'Zend\Mvc\Router\Http\Method',
'options' => array(
'verb' => 'post',
'defaults' => array(
),
),
'child_routes'=>array(
//...
),
),

Above, we define the route which has the Method type. It takes the verb option, which may be
the comma-separated list of acceptable HTTP verbs (like get, post, put, etc.)

5.10 Extracting Parameters from Route


On route match, the router (top-level route class) returns some parameters: the defaults (parameters listed in the defaults section of routing configuration) plus any wildcard parameters
extracted from URL string.
In your controller, you will often need to retrieve these parameters. We already did this in the
examples above. In this section, we will give some summary.

127

URL Routing

To retrieve a parameter from the route in your controllers action method, you typically use the
Params controller plugin and its fromRoute() method, which takes two arguments: the name of
the parameter to retrieve and the value to return if the parameter is not present.
The fromRoute() method can also be used to retrieve all parameters at once as an array. To do
that, call the fromRoute() without arguments, as shown in the example below:
// An example action.
public function someAction() {
// Get the single 'id' parameter from route.
$id = $this->params()->fromRoute('id', -1);
// Get all route parameters at once as an array.
$params = $this->params()->fromRoute();
//...
}

5.10.1 Retrieving the RouteMatch and the Router Object


On route match, the router class internally creates an instance of Zend\Mvc\Router\RouteMatch
class, providing the methods for extracting the matched route name and parameters extracted
from route. The useful methods of the RouteMatch class are listed in table 5.3:
Table 5.3. ZendMvcRouterRouteMatch class methods

Method Name

Description

getMatchedRouteName()
getParams()
getParam($name, $default)

Gets the name of matched route.


Get all parameters.
Get a specific parameter.

In most cases, it will be sufficient to use the Params controller plugin, but alternatively
you can use the RouteMatch object for accomplishing the same task.

To get the RouteMatch object from your controllers action method, you can use the following
code:

URL Routing
1
2

128

// An example action.
public function someAction() {

// Get the RouteMatch object.


$routeMatch = $this->getEvent()->getRouteMatch();

4
5
6

// Get matched route's name.


$routeName = $routeMatch->getMatchedRouteName();

7
8
9

// Get all route parameters at once as an array.


$params = $routeMatch->getParams();

10
11
12

//...

13
14

In line 5 of the code above, we use the getEvent() method of the AbstractActionController
base class to retrieve the MvcEvent object, which represents the event (in ZF2, the application life
cycle consists of events). We then use the the getRouteMatch() method of the MvcEvent class to
retrieve the RouteMatch object.
In line 8, we use the getMatchedRouteName() method to retrieve the name of the route that
matched the HTTP request, and in line 11 we retrieve all the parameters from the route.
The MvcEvent class can also be used for retrieving the router (the top-level route class). You can
do this with the getRouter() method of the MvcEvent class, as below:
// Call this inside of your action method
// to retrieve the RouteStackInterface for the router class.
$router = $this->getEvent()->getRouter();

In the code above, we use the getRouter() method, which returns the RouteStackInterface
interface. This interface is the base interface for both SimpleRouteStack and TreeRouteStack,
and it provides the methods for working with the routes contained inside the route stack.

5.11 Generating URLs from Route


The main task of any route class is to compare the route string with the request URL and on
match return the set of parameters by which a controller and action can be determined. An
opposite task a route class allows to do is generating a URL by parameters. This feature can be
used in your controller action methods for generating URLs, for example, for redirecting a user
to another page. It can also be used inside view templates for generating hyperlinks.

5.11.1 Generating URLs in View Templates


Your web pages usually contain hyperlinks to other pages. These links may point either to a
page internal to your site or to a page on another site. A hyperlink is represented by <a> HTML

129

URL Routing

tag having href attribute specifying the URL of the destination page. Below, an example of a
hyperlink pointing to an external page is presented:
<a href="http://example.com/path/to/page">A link to another site page</a>

When you generate a hyperlink to a resource internal to your site, you typically use relative URL
(without host name):
<a href="/path/to/internal/page">A link to internal page</a>

To generate URLs in your view templates (.phtml files), you can use the Url view helper class,
which takes the route name as an input argument:
1
2

<!-- A hyperlink to Home page -->


<a href="<?php echo $this->url('home'); ?>">Home page</a>

3
4
5

<!-- A hyperlink to About page -->


<a href="<?php echo $this->url('about'); ?>">About page</a>

In the lines above, we generate two relative URLs. In line 2, we call the Url view helper and
pass the home route name as its parameter. In line 5, we pass the about route name as an
argument for the Url view helper.
In the example above, the Url view helper internally uses the RouteMatch object and
calls the Literal route to assemble the URL string by route name.

After the PhpRenderer class executes the view templates code, the output HTML markup will
be the following:
<!-- A hyperlink to Home page -->
<a href="/">Home page</a>
<!-- A hyperlink to About page -->
<a href="/about">About page</a>

5.11.1.1 Passing Parameters


If a route uses some variable parameters, you should pass them to the Url view helper as the
second argument:

URL Routing
1
2
3
4

130

<!-- A hyperlink to About page -->


<a href="<?php echo $this->url('application/default',
array('controller' => 'index', 'action' => 'about')); ?>" >
About page </a>

5
6
7
8
9
10

<!-- A hyperlink to Barcode image -->


<a href="<?php echo $this->url('application/default', array(
'controller' => 'index', 'action' => 'barcode',
'type' => 'code39', 'text' => 'HELLO-WORLD')); ?>" >
Barcode image </a>

In the example above, we use Url view helper to generate the two URLs by route name and
parameters. We pass the application/default compound route name as the first argument, and
an array of parameters as the second argument.
In line 3, we pass the controller and action parameters to tell the Segment route class that it
should substitute the corresponding wildcards in the route string with the index and about
strings.
After the PhpRenderer class executes the view templates code, the output HTML markup will
be the following:
<!-- A hyperlink to About page -->
<a href="/application/index/about" > About page </a>
<!-- A hyperlink to Barcode image -->
<a href="/application/index/barcode/code39/HELLO-WORLD" > Barcode image </a>

As another example, lets try to generate a URL for our Regex route (the one which serves our
static pages):
<!-- A hyperlink to Introduction page -->
<a href="<?php echo $this->url('doc', array('page'=>'introduction')); ?>">
Introduction </a>

This will generate the following HTML markup:


<!-- A hyperlink to Introduction page -->
<a href="/doc/introduction.html"> Introduction </a>

5.11.1.2 Generating Absolute URLs


If you need to generate an absolute URL (having the scheme and host name), you can specify
the third parameter for the Url view helper. The third parameter should be an array containing
one or several options. For assembling the absolute URL, pass the force_canonical option, as in
the example below:

URL Routing
1
2
3

131

<!-- A hyperlink to Home page -->


<a href="<?php echo $this->url('home', array(),
array('force_canonical' => true)); ?>" > Home page </a>

4
5
6
7
8

<!-- A hyperlink to About page -->


<a href="<?php echo $this->url('application/default', array(
'controller' => 'index', 'action' => 'about'),
array('force_canonical' => true)); ?>" > About page </a>

In lines 2-3 of the example above, we pass the home route name as the first argument, empty
array as the second argument, and an array containing force_canonical option as the third
argument. In lines 6-8, we also pass the force_canonical option as the third argument for
generating the URL of the About page.
The resulting HTML markup of the code above will be as follows:
<!-- A hyperlink to Home page -->
<a href="http://localhost/" > Home page </a>
<!-- A hyperlink to About page -->
<a href="http://localhost/application/index/about" > About page </a>

5.11.1.3 Specifying Query Part


If you want your URL to have a query part, you can specify the query option in the third
argument of the Url view helper. For example, assume you have the search action in some
controller (and a routing rule mapped to this action), and you want to pass it search query
string and count of output results per page. The URL for this action would be like this:
http://localhost/search?q=topic&count=10. To generate such a URL, you can use the following
code:
<a href="<?php echo $this->url('search', array(),
array('force_canonical' => true,
'query'=>array('q'=>'topic', 'count'=>10))); ?>" >
Search </a>

In the code above, we specified the query option, which is the array containing namevalue
pairs of the query parameters.

5.11.2 Generating URLs in Controllers


You can generate URLs inside your controllers action methods using the Url controller plugin.
To generate a URL, you call the Url controller plugins fromRoute() method, as in the example
below:

132

URL Routing
1
2

// An example action method


public function someAction() {

// Generate a URL pointing to the Home page ('/')


$url1 = $this->url()->fromRoute('home');

4
5
6

// Generate an absolute URL pointing to the About page


// ('http://localhost/application/index/about')
$url2 = $this->url()->fromRoute('application/default',
array('controller'=>'index', 'action'=>'about'),
array('force_canonical'=>true));

7
8
9
10
11
12

The arguments the Url plugin takes and their meaning are identical to the Url view
helpers ones. So, you can generate absolute or relative URLs the same way you did in
your view templates.

5.11.3 URL Encoding


When generating URLs either with the Url view helper or with the Url controller plugin, you
should remember that URLs may only contain safe characters from ASCII character set. Thus,
if you pass the parameter containing unsafe characters, these characters will be replaced with
the sequence of the percentage character and two digits.
For example, lets try to generate a URL for our Regex route and pass it the page parameter
with the value /chapter1/introduction.
<!-- A hyperlink to Introduction page -->
<a href="<?php echo $this->url('doc',
array('page'=>'chapter1/introduction')); ?>">
Introduction </a>

We could assume it generates the URL like /doc/chapter1/introduction.html. But because the
slash (/) character is unsafe, it will be replaced with the %2F characters for security reasons,
and we will have the following HTML code:
<!-- A hyperlink to Introduction page -->
<a href="/doc/chapter1%2Fintroduction.html"> Introduction </a>

Unfortunately, this hyperlink is unusable, because it wont match our Regex route.

133

URL Routing

5.12 Writing Own Route Type


Although ZF2 provides you with many route types, in some situations, you will need to write
your own route type.
One example of the need for such a custom route type is when you have to define the URL
mapping rules dynamically. Usually, you store the routing rules configuration in modules config
file, but in some CMS systems you will have documents stored in the database. For such a system,
you would need to develop a custom route type which would connect to the database and perform
route matching against the data stored in the database. You cannot store this information in
config file, because new documents are created by system administrators, not programmers.

5.12.1 RouteInterface
We know that every route class must implement the Zend\Mvc\Router\Http\RouteInterface
interface. The methods of this interface are presented in table 5.4:
Table 5.4. RouteInterface methods

Method Name

Description

factory($options)

Static method for creation of the route class.

match($request)

Method which performs match against the HTTP request data.

assemble($params, $options)

Method for generating URL by route parameters.

getAssembledParams()

Method for retrieving parameters that were utilized for URL


generation.

The static factory() method is used by the ZF2 router (TreeRouteStack or SimpleRouteStack)
for instantiating the route class. The router passes the options array an argument for the
factory() method.
The match() method is used to perform the matching of the HTTP request (or, particularly its
URL) against the options data passed to the route class through the factory(). The match()
method should return either an instance of the RouteMatch class on successful match, or null
on failure.
The assemble() method is used for generating URL string by route parameters and options. The
getAssembledParams() helper methods purpose is to return the array of parameters which were
used on URL generation.

5.12.2 Custom Route Class


To demonstrate the creation of a custom route type, lets improve our previous approach to
building the simple documentation system with Regex route type. The disadvantage of the Regex
route type is that you cannot organize the static pages in a hierarchy by creating subdirectories
under the doc directory (when generating an URL for such a page, the slash directory separator

134

URL Routing

will be URL-encoded making the hyperlink unusable). We will create our custom StaticRoute
class that allows to fix this issue.
Moreover, the class we will create is more powerful, because it will not only recognize URLs
starting with /doc and ending with .html, but it will also recognize generic URLs, like /help
or /support/chapter1/introduction.
What we want to achieve:
The StaticRoute class should be insertable to the route stack (to SimpleRouteStack or to
TreeRouteStack) and usable together with other route types.
The route class should recognize generic URLs, like /help or /introduction.
The route class should match the URL against the directory structure. For example, if
the URL is /chapter1/introduction, then the route should check if the corresponding
view template file <base_dir>/chapter1/introduction.phtml exists and is readable, and if
so, report match. If the file does not exist (or not readable), return the failure status.
The route class should check the URL for acceptable file names using a regular expression.
For example, the file name introduction is acceptable, but the name *int$roduction is
not. If the file name is not acceptable, the failure status should be returned.
The route should be able to assemble the URL string by route name and parameters.
To start, create the Service subdirectory under the modules source directory and put the
StaticRoute.php file inside of it (figure 5.9). Inside that file, paste the stub code presented below:

Figure 5.9. StaticRoute.php file

URL Routing
1
2

135

<?php
namespace Application\Service;

3
4
5
6
7
8
9

use
use
use
use
use
use

Traversable;
\Zend\Mvc\Router\Exception;
\Zend\Stdlib\ArrayUtils;
\Zend\Stdlib\RequestInterface as Request;
\Zend\Mvc\Router\Http\RouteInterface;
\Zend\Mvc\Router\Http\RouteMatch;

10
11
12
13
14
15
16

// Custom route that serves "static" web pages.


class StaticRoute implements RouteInterface
{
// Create a new route with given options.
public static function factory($options = array()) {
}

17

// Match a given request.


public function match(Request $request, $pathOffset = null) {
}

18
19
20
21

// Assembles a URL by route params.


public function assemble(array $params = array(), array $options = array()) {
}

22
23
24
25

// Get a list of parameters used while assembling.


public function getAssembledParams() {
}

26
27
28
29

From the code above, you can see that we placed the StaticRoute class inside the Application\Service
namespace (line 2), because it can be treated as a service model.
In lines 4-9, we define some class name aliases for making the class names shorter.
In lines 11-28, we define the stub for the StaticRoute class. The StaticRoute class implements
the RouteInterface interface and defines all the methods specified by the interface: factory(),
match(), assemble() and getAssembledParams().
Next, lets add several protected properties and the constructor method to the StaticRoute class,
as shown below:

URL Routing
1
2

136

<?php
//...

3
4
5
6
7

class StaticRoute implements RouteInterface


{
// Base view directory.
protected $dirName;

// Path prefix for the view templates.


protected $templatePrefix;

9
10
11

// File name pattern.


protected $fileNamePattern = '/[a-zA-Z0-9_\-]+/';

12
13
14

// Defaults.
protected $defaults;

15
16
17

// List of assembled parameters.


protected $assembledParams = array();

18
19
20

// Constructor.
public function __construct($dirName, $templatePrefix,
$fileNamePattern, array $defaults = array())
{
$this->dirName = $dirName;
$this->templatePrefix = $templatePrefix;
$this->fileNamePattern = $fileNamePattern;
$this->defaults = $defaults;
}

21
22
23
24
25
26
27
28
29
30

// ...

31
32

Above, in line 7, we define the $dirName property that is intended for storing the name of
the base directory where the static view templates will be located. In line 10, we define the
$templatePrefix class variable for storing the prefix for prepending to all view template names.
Line 13 contains the $fileNamePattern variable that will be used for checking the file name.
In lines 22-29, we define the constructor method that is called on instance creation for initializing
the protected properties.
Next, lets implement the factory() method for our StaticRoute custom route class. The
factory() method will be called by the router for instantiating the route class:

URL Routing
1
2

137

<?php
//...

3
4
5
6

class StaticRoute implements RouteInterface


{
//...

7
8
9
10
11
12
13
14
15
16

// Create a new route with given options.


public static function factory($options = array())
{
if ($options instanceof Traversable) {
$options = ArrayUtils::iteratorToArray($options);
} elseif (!is_array($options)) {
throw new Exception\InvalidArgumentException(__METHOD__ .
' expects an array or Traversable set of options');
}

17

if (!isset($options['dir_name'])) {
throw new Exception\InvalidArgumentException(
'Missing "dir_name" in options array');
}

18
19
20
21
22

if (!isset($options['template_prefix'])) {
throw new Exception\InvalidArgumentException(
'Missing "template_prefix" in options array');
}

23
24
25
26
27

if (!isset($options['filename_pattern'])) {
throw new Exception\InvalidArgumentException(
'Missing "filename_pattern" in options array');
}

28
29
30
31
32

if (!isset($options['defaults'])) {
$options['defaults'] = array();
}

33
34
35
36

return new static(


$options['dir_name'],
$options['template_prefix'],
$options['filename_pattern'],
$options['defaults']);

37
38
39
40
41
42

In the code above, we see that the factory() method takes the options array as the argument
(line 9). The options array may contain the options for configuring the route class. The
StaticRoute class will accept the following options:

URL Routing

138

dir_name - the base directory where to store all static view templates.
template_prefix - the prefix to prepend to all template names.
filename_pattern - the regular expression for checking the file names.
defaults - parameters returned by router by default.

Once we parsed the options, in lines 37-41 we call the class constructor method to instantiate
and return the StaticRoute object.
The next method we add to the StaticRoute route class is the match() method:
1
2

<?php
//...

3
4
5
6

class StaticRoute implements RouteInterface


{
//...

7
8
9
10
11
12
13
14

// Match a given request.


public function match(Request $request, $pathOffset=null)
{
// Ensure this route type is used in an HTTP request
if (!method_exists($request, 'getUri')) {
return null;
}

15
16
17
18

// Get the URL and its path part.


$uri = $request->getUri();
$path = $uri->getPath();

19
20
21

if($pathOffset!=null)
$path = substr($path, $pathOffset);

22
23
24

// Get the array of path segments.


$segments = explode('/', $path);

25
26
27
28
29
30
31
32

// Check each segment against allowed file name template.


foreach ($segments as $segment) {
if(strlen($segment)==0)
continue;
if(!preg_match($this->fileNamePattern, $segment))
return null;
}

33
34
35
36

// Check if such a .phtml file exists on disk


$fileName = $this->dirName . '/'.
$this->templatePrefix.$path.'.phtml';

URL Routing

139

if(!is_file($fileName) || !is_readable($fileName)) {
return null;
}

37
38
39
40

$matchedLength = strlen($path);

41
42

// Prepare the RouteMatch object.


return new RouteMatch(array_merge(
$this->defaults,
array('page'=>$this->templatePrefix.$path)
),
$matchedLength);

43
44
45
46
47
48

49
50

In the code above, we see that the match() method takes two arguments: the HTTP request object
(an instance of Zend\Stdlib\Request class) and the URL path offset. The request object is used
for accessing the request URL (line 17). The path offset parameter is a non-negative integer,
which points to the portion of the URL the route is matched against (line 21).
In line 24, we extract the segments from URL. Then we check if every segment is an acceptable
file (directory) name (lines 27-321). If the segment is not a valid file name, we return null as a
failure status.
In line 35, we calculate the path to the view template, and in lines 27-29 we check if such a
file really exists and accessible for reading. This way we match the URL against the directory
structure.
In lines 44-48, we prepare and return the RouteMatch object with the default parameters plus the
page parameter containing the view template name for rendering.
To complete the implementation of our StaticRoute class, we add the assemble() and getAssembledParams()
methods, that will be used for generation of URLs by route parameters. The code for these
methods is presented below:
1
2

<?php
//...

3
4
5
6

class StaticRoute implements RouteInterface


{
//...

7
8
9
10
11
12
13

// Assembles a URL by route params


public function assemble(array $params = array(),
array $options = array())
{
$mergedParams = array_merge($this->defaults, $params);
$this->assembledParams = array();

URL Routing

140

14

if(!isset($params['page'])) {
throw new Exception\InvalidArgumentException(__METHOD__ .
' expects the "page" parameter');
}

15
16
17
18
19

$segments = explode('/', $params['page']);


$url = '';
foreach($segments as $segment) {
if(strlen($segment)==0)
continue;
$url .= '/' . rawurlencode($segment);
}

20
21
22
23
24
25
26
27

$this->assembledParams[] = 'page';

28
29

return $url;

30

31
32

// Get a list of parameters used while assembling.


public function getAssembledParams()
{
return $this->assembledParams;
}

33
34
35
36
37
38

In the code above, we define the assemble() method, which takes the two arguments: the
parameters array and the options array (line 10). The method constructs the URL by encoding
the segments with URL encoding and concatenating them (line 20-26).
The method getAssembledParams() just returns the names of the parameters we used for URL
generation (page 36).
Now weve finished the StaticRoute route class. To use our custom route type, we add the
following configuration to the module.config.php configuration file:
1
2
3
4
5
6
7
8
9
10
11
12

'static' => array(


'type' => '\Application\Service\StaticRoute',
'options' => array(
'dir_name'
=> __DIR__ . '/../view',
'template_prefix' => 'application/index/static',
'filename_pattern' => '/[a-z0-9_\-]+/',
'defaults' => array(
'controller' => 'Application\Controller\Index',
'action'
=> 'static',
),
),
),

URL Routing

141

In line 1 of the configuration above, we define the routing rule named static. The type
parameter defines the full StaticRoute class name for the rule (line 2). In the options array, we
define the base directory where the static pages will be placed (line 4), the template prefix (line
5), the filename pattern (line 6), and the defaults array, containing the name of the controller
and the action that will serve all the static pages.
The final step is creating the action method in the IndexController class:
1

public function staticAction() {

// Get path to view template from route params


$pageTemplate = $this->params()->fromRoute('page', null);
if($pageTemplate==null) {
$this->getResponse()->setStatusCode(404);
return;
}

3
4
5
6
7
8
9

// Render the page


$viewModel = new ViewModel();
$viewModel->setTemplate($pageTemplate);
return $viewModel;

10
11
12
13
14

The action above is almost identical to the action we used for the Regex route. In line 4, we
retrieve the page parameter from route and save it as the $pageTemplate variable. In line 121,
we create the ViewModel variable container, and in line 12 we explicitly set the view template
name for rendering.
To see the system in action, lets add a couple of static view pages: the Help page (help.phtml)
and the introduction page (intro.phtml). Create the static subdirectory under the view/application/index directory of the Application module and put the help.phtml view template there:
1

<h1>Help</h1>

2
3

<p>

See the help <a href="<?php echo $this->url('static',


array('page'=>'/chapter1/intro')); ?>">introduction</a> here.
</p>

5
6

Then create the chapter1 subdirectory in the static directory and put the following chapter1/intro.phtml file in there:

142

URL Routing

<h1>Introduction</h1>
<p>
Write the help introduction here.
</p>

Finally, you should receive the following directory structure (see figure 5.10):

Figure 5.10. Static pages

Eventually, open the following URL in your browser: http://localhost/help. The Help page should
appear (see figure 5.11 for example). If you type the http://localhost/chapter1/intro URL in your
browser, you should see the Introduction page (figure 5.12).

Figure 5.11. Help page

143

URL Routing

Figure 5.12. Introduction page

You can create static pages just by adding the phtml files under the static directory, and they will
automatically become available to site users.
If you are stuck, you can find this complete working example inside the Hello World
application.

5.13 Summary
In this chapter, weve learned about routing. Routing is used for mapping HTTP request to
controllers action method. There are several route types (Literal, Segment, Regex, Hostname,
Scheme, Method etc.). Each route type uses different URL segments (and, possibly, other HTTP
requests data) to compare the URL with the specified route template. We also learned how to
write custom route class if the capabilities of standard route types are not sufficient.
The main task of a route class is to return a route match containing the set of parameters, by
which a controller and action can be determined. An opposite task a route class allows to do is
generating a URL by parameters. This feature is widely used in view layer of the application for
generating hyperlinks.
Route types can be combined in a nested tree with the help of TreeRouteStack router, or
organized in a chain with SimpleRouteStack router. These two routers allow to define arbitrarily
complex rules.
Routing configuration is stored in modules configuration file under the router key. Each
module exposes its own routing rules, which are merged with other modules configuration
upon application start up.

6 Page Appearance and Layout


In this chapter you will learn how to make your web pages attractive and professionally looking
with the help of Twitter Bootstrap CSS Framework and how to position elements on a page
using ZF2 layout mechanism. Youll also become familiar with common view helpers allowing
for composing web pages of reusable parts. If you are new to Twitter Bootstrap, it is also
recommended that you refer to Appendix C for advanced description of Bootstrap capabilities.
ZF2 components covered in this chapter:
Component

Description

Zend\Mvc

Support of MVC pattern. Implements base controller classes, controller plugins, etc.

Zend\View

Implements the functionality for variable containers, rendering a web page and
common view helpers.

6.1 About CSS Stylesheets and Twitter Bootstrap


In a ZF2-based web site, for defining the visual appearance and style of the web pages, CSS
stylesheets are utilized. These CSS files are typically stored in APP_DIR/public/css directory.
Because the CSS rules may be rather complex and require laborious adjustment and the skills
of a designer, they can be separated in a library (framework). Analogous to PHP frameworks,
CSS frameworks allow for code reusability.
Today, several CSS frameworks exist on the market, and one of them is Twitter Bootstrap (or
shortly, the Bootstrap). Originally designed at Twitter to unify the appearance of their own
web tools, Bootstrap has became a popular CSS framework, allowing to make your web site
professionally looking and visually appealing, even if you dont have advanced designer skills
and without the need of creating basic CSS rules (but, of course you can define your own custom
CSS rules on top of Bootstrap to customise your sites appearance). Bootstrap is freely distributed
under the Apache License v.2.0.
Twitter Bootstrap is shipped with Zend Skeleton Application, so you can use it out of
the box. Alternatively, you can download the newest version of Bootstrap from the
projects official page. At the moment of writing this book, the latest version is v.3.0.

Generally, the Bootstrap does the following things:


If you are new to CSS, please refer to the excellent W3Schools CSS tutorial by visiting this link.
http://twitter.github.io/bootstrap/
http://www.apache.org/licenses/LICENSE-2.0.html
http://getbootstrap.com/

Page Appearance and Layout

145

It provides the CSS reset that is a style sheet defining styles for all possible HTML elements.
This ensures your web site will look the same way in all web browsers.
It provides the base CSS rules that define style of typography (headings and text), tables,
forms, buttons, images and so on.
It defines the grid system. The grid system allows to arrange elements on your web page
in a grid-like structure. For example, look at the Skeleton Applications main page (figure
6.1), where we have the grid consisting of three columns.
It defines useful web interface components like dropdown menus, navigation bars, breadcrumbs, pagination and so on. For example, on the skeleton apps main page, there is
the navigation bar component at the top, and the header (also called the Hero Unit or
Jumbotron) component below the navbar. These components are very handy on any web
site.
In includes the JavaScript extensions that allow to make Bootstrap-provided interface
components more interactive. For example, JavaScript is used to animate dropdown menus
and display modal dialogs.

Figure 6.1. Main page of the skeleton app and its layout

If you are new to Twitter Bootstrap, it is recommended that you refer to Appendix
C, where you can find more information about using Twitter Bootstrap and its
components.

146

Page Appearance and Layout

6.2 Page Layout in Zend Framework 2


Pages of your web site typically have some common structure that can be shared among them.
For example, a typical page has the <!DOCTYPE> declaration to identify the HTML document, and
the <head> and <body> elements:
Typical page structure

<!DOCTYPE html>
<html lang="en">
<head>
<title>Welcome</title>
<!-- Include metas, stylesheets and scripts here -->
</head>
<body>
<!-- Include page content here -->
</body>
</html>

The <head> element contains the page title text, meta information and references to included
stylesheets and scripts. The <body> element contains the content of the page, like the logo image,
the navigation bar, the page text, and the footer with copyright information.
In Zend Framework 2, you define this common structure with the master view template called
the layout. The layout decorates other view templates.
The layout template typically has a placeholder in which ZF2 puts the content specific to a
particular page (see figure 6.2 for example).

Figure 6.2. Content placeholder in layout template

In the Skeleton Application, the default layout template file is called layout.phtml and is located
inside of the view/layout directory in Application modules directory (see figure 6.3 for example).

147

Page Appearance and Layout

Lets look at the layout.phtml template file in more details. Below, the complete contents of the
file is presented (because some lines of the file are too long for a book page, line breaks are
inserted where necessary):

Figure 6.3. Layout directory


1

<?php echo $this->doctype(); ?>

2
3
4
5
6
7
8
9
10

<html lang="en">
<head>
<meta charset="utf-8">
<?php echo $this->headTitle('ZF2 '.
$this->translate('Skeleton Application'))
->setSeparator(' - ')
->setAutoEscape(false)
?>

11
12
13
14
15

<?php echo $this->headMeta()


->appendName('viewport', 'width=device-width, initial-scale=1.0')
->appendHttpEquiv('X-UA-Compatible', 'IE=edge')
?>

16
17
18
19
20
21
22
23
24

<!-- Le styles -->


<?php
echo $this->headLink(array('rel' => 'shortcut icon',
'type' => 'image/vnd.microsoft.icon',
'href' => $this->basePath().'/img/favicon.ico'))
->prependStylesheet($this->basePath().'/css/style.css')
->prependStylesheet($this->basePath().'/css/bootstrap-theme.min.css')
->prependStylesheet($this->basePath().'/css/bootstrap.min.css') ?>

25
26

<!-- Scripts -->

Page Appearance and Layout


27
28
29
30
31
32
33
34

<?php echo $this->headScript()


->prependFile($this->basePath().'/js/bootstrap.min.js')
->prependFile($this->basePath().'/js/jquery.min.js')
->prependFile($this->basePath().'/js/respond.min.js', 'text/javascript',
array('conditional' => 'lt IE 9',))
->prependFile($this->basePath().'/js/html5shiv.js', 'text/javascript',
array('conditional' => 'lt IE 9',));
?>

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse"
data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="<?php echo $this->url('home') ?>">
<img src="<?php echo $this->basePath('img/zf2-logo.png') ?>"
alt="Zend Framework 2"/>&nbsp;
<?php echo $this->translate('Skeleton Application') ?>
</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="<?php echo $this->url('home') ?>">
<?php echo $this->translate('Home') ?></a>
</li>
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>
<div class="container">
<?php echo $this->content; ?>
<hr>
<footer>
<p>
&copy; 2005 - <?php echo date('Y') ?> by Zend Technologies Ltd.
<?php echo $this->translate('All rights reserved.') ?>
</p>
</footer>
</div> <!-- /container -->
<?php echo $this->inlineScript() ?>

148

Page Appearance and Layout


73
74

149

</body>
</html>

You can see that the layout.phtml file (as a usual view template) consists of HTML tags
mixed with PHP code fragments. When the template is rendered, ZF2 evaluates the inline PHP
fragments and generates resulting HTML page visible to site users.
Line 1 above generates the <!DOCTYPE> declaration of the HTML page with the Doctype view
helper.
Line 3 defines the <html> element representing the root of the HTML document. The <html> tag
is followed by the <head> tag (line 4), which typically contains a title for the document, and can
include other information like scripts, CSS styles and meta information.
In line 5, the <meta> tag provides the browser with a hint that the document is encoded using
UTF-8 character encoding.
In line 6, we have the HeadTitle view helper that allows to define the title for the page
(ZF2 Skeleton Application). The title will be displayed in the web browsers caption. The
setSeparator() method is used to define the separator character for the compound page titles;
the setAutoEscape() method enhances the security by escaping unsafe characters from the page
title. The Translate view helper is used for localizing your web sites strings into different
languages, and described in more details in Chapter 11.
In line 12, the HeadMeta view helper allows to define the <meta name="viewport"> tag containing
meta information for the web browser to control layout on different display devices, including
mobile browsers. The width property controls the size of the viewport, while the initial-scale
property controls the zoom level when the page is loaded. This makes the web page layout
responsive to device viewport size.
In line 19, the HeadLink view helper allows to define the <link> tags. With the <link> tags, you
typically define the favicon for the page (located in APP_DATA\public\img\favicon.ico file)
and the CSS stylesheets.
In lines 22-24, the stylesheets common to all site pages are included by the prependStylesheet()
method of the HeadLink view helper. Any page in our web site will load three CSS stylesheet
files: bootstrap.min.css (the minified version of Twitter Bootstrap CSS Framework), bootstraptheme.min.css (the minified Bootstrap theme stylesheet) and style.css (CSS file allowing us to
define our own CSS rules overriding Bootstrap CSS rules).
Lines 27-35 include the JavaScript files that all your web pages will load. The scripts are executed
by the clients web browser, allowing to introduce some interactive features for your pages.
We use the the bootstrap.min.js (minified version of Twitter Bootstrap) and jquery.min.js
(minified version of jQuery library) scripts. All scripts are located in APP_DIR/public/js directory.
Line 38 defines the <body> tag, the documents body which contains all the contents of the
document, such as the navigation bar, text, hyperlinks, images, tables, lists, etc.
The <!DOCTYPE> declaration goes first in the HTML document, before the <html> tag. The declaration provides an instruction to the web
browser about what version of HTML the page is written in (in our web site, we use HTML5-conformant document type declaration).
The UTF-8 allows to encode any character in any alphabet around the world, thats why it is recommended for encoding the web pages.
A compound page title consists of two parts: the first part (Zend Skeleton Application) is defined by the layout, and the second part defined by a particular page - is prepended to the first one. For example, for the About page of your site you will have the About - Zend Skeleton
Application, and for the Documentation page you will have something like Documentation - Zend Skeleton Application.

Page Appearance and Layout

150

In lines 39-63, you can recognize the Bootstrap navigation bar definition. The skeleton application
uses the collapsible navbar with dark inverse theme. The navbar contains the single link Home.
If you look at lines 63-72, you should notice the <div> element with container class which
denotes the container element for the grid system. So, you can use the Bootstrap grid system to
arrange the contents of your pages.
Line 64 is very important, because this line defines the inline PHP code that represents the page
content placeholder we talked about in the beginning of this section. When the ZF2 page renderer
evaluates the layout template, it echoes the actual page content here.
Lines 65-71 define the page footer area. The footer contains the copyright information like 2013
by Zend Technologies Ltd. All rights reserved. You can replace this information with you own
company name.
Line 73 is the placeholder for JavaScript scripts loaded by the concrete page. The InlineScript
view helper will substitute here all the scripts you register (about registering JavaScript scripts,
you will see it later in this chapter).
And finally, lines 74-75 contain the closing tags for the body and the HTML document.

6.3 Modifying the Default Page Layout


To demonstrate how you can define your own page layout, we will modify the original layout of
the Zend Skeleton Application page. We want to make it display the Hello world page title, the
Hello world! header text at the top, the navigation bar and breadcrumbs below the header, page
content placeholder in the middle of the page, and the footer with the copyright information at
the bottom (see figure 6.4 for an example of what we are trying to achieve).
Lets start with the Hello World page title. We replace the lines 6-10 in the layout.phtml file as
follows:
<?php
echo $this->headTitle('Hello World')
->setSeparator(' - ')
->setAutoEscape(false)
?>

Next, we will use the Bootstrap-provided grid system for arranging the main blocks on the page.
Replace the HTML code of the <body> element (lines 37-73) with the following one:

151

Page Appearance and Layout

Figure 6.4. Resulting Page Layout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

<body>
<div class="container">
<div class="row">
<!-- Page header -->
<div class="col-md-4">
<div class="app-caption">Hello World!</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<!-- Navigation bar -->
</div>
</div>
<div class="row">
<div class="col-md-12">
<!-- Breadcrumbs -->
</div>
</div>
<div class="row">
<div class="col-md-12">

Page Appearance and Layout


21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

152

<!-- Page content placeholder -->


<?php echo $this->content; ?>
</div>
</div>
<div class="row">
<div class="col-md-12">
<hr>
<p>
&copy; <?php echo date('Y') ?> by Your Company.
<?php echo $this->translate('All rights reserved.') ?>
</p>
</div>
</div> <!-- /container -->
<?php echo $this->inlineScript() ?>
</body>

In the code above, we defined the <div> element with the container class and put the <div>
elements of the grid inside of it. The grid consists of 5 rows:
The page header containing the Hello World! text (lines 3-8). The header text spans four
grid columns. For styling the text, we use our custom CSS class app-caption (we will
define this class in style.css file a little bit later).
We left the space for navigation bar interface component in line 11.
In line 16, we have the space for breadcrumbs component.
In line 22, we have the page content placeholder. When the renderer evaluates the page, it
will echo the value of the $content variable, so the actual page content will be substituted
here.
And in lines 25-32, we provided the page footer with the text (c) 2013 by Your Company.
All rights reserved. You can change this text and substitute your company name here, if
you wish.
Next, we put the navigation bar in the corresponding grid row:
<!-- Navigation bar -->
<nav class="navbar navbar-default" role="navigation">
<div class="collapse navbar-collapse navbar-ex1-collapse">
<ul class="nav navbar-nav">
<li class="active">
<a href="<?php echo $this->url('home') ?>">Home</a>
</li>
<li>
<a href="<?php echo $this->url('application/default',
array('controller'=>'index', 'action'=>'downloads')) ?>">
Downloads
</a>

Page Appearance and Layout

153

</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Support <b class="caret"></b>
<ul class="dropdown-menu">
<li>
<a href="<?php echo $this->url('doc',
array('page'=>'contents')) ?>">
Documentation
</a>
</li>
<li>
<a href="<?php echo $this->url('static',
array('page'=>'help')) ?>">
Help
</a>
</li>
</ul>
</a>
</li>
<li>
<a href="<?php echo $this->url('about') ?>">About</a>
</li>
</ul>
</div>
</nav>

In the code above, we used the navbar interface component provided by the Bootstrap. We also
used the Url view helper to insert the links to the navigation items.
We discussed the usage of the Url view helper in the Generating URLs from Route
section in Chapter 5.

Next, put the breadcrumbs component to the corresponding grid row:


<!-- Breadcrumbs -->
<ol class="breadcrumb">
<li class="active">Home</li>
</ol>

Finally, we need to provide a couple of custom CSS rules to fine-tune the look and feel. We define
our own CSS rules in the style.css stylesheet.
We want to make the Hello World! header text to use larger bold font and use a nice looking
color. To do this, open the style.css file, and append the following lines to the end:

Page Appearance and Layout

154

div.app-caption {
padding: 25px 0px;
font-size:3.0em;
font-weight: bold;
color:#6aacaf
}

In the CSS code above, we created the app-caption class which can be applied to <div> element
and defining the 25 pixels vertical padding, large font size, bold text style and the hexadecimal
representation of the RGB text color.
By default, in skeleton application, the navbar is pinned to page top, and the CSS rule for the page
body defines the 20 pixels top padding to leave space for it. Since in our Hello World example
weve unpinned the navigation bar from top of the page and placed it in page flow, we need to
remove the padding from page body top. To do that, edit the following CSS rule in the style.css
file and make it look like the one below:
body {
padding-bottom: 40px;
}

Great, weve completed the page layout template! To see the result of our changes, open the site
in your browser, you should see the page as in figure 6.4. You can click the links in navigation
bar to visit the pages like About or Documentation, etc. The content of a particular page is put
into the content placeholder of our layout.
The result can be seen in action in the Hello World sample application that is part of
this books example code available on GitHub.

6.4 Switching between Layouts


By default, ZF2 provides you with a single layout template layout.phtml. In real-life applications,
you will probably need to have several layouts and switch the layout for certain controller/action.
For example, you may have a front-end and a back-end part of your site. The front-end part
would consist of web pages publicly visible to all users and would utilize the default layout for
all of these pages. The back-end part would consist of pages visible to the administrator user only
and utilize another layout template containing an administrative menu.
First, prepare another layout template file. For example, call it layout2.phtml. To simplify the file
preparation, copy the content of the default layout.phtml file and make the necessary changes.
When the second layout template is ready, you can switch between layouts for a particular
controllers action by using the following code:

Page Appearance and Layout


1
2
3
4

155

// A controller's action method that uses an alternative


// layout template.
public function indexAction() {
//...

// Use the Layout plugin to access the ViewModel


// object associated with layout template.
$this->layout()->setTemplate('layout/layout2');

6
7
8
9

//...

10
11

In the example action method above, we use the Layout controller plugin (line 8) that allows
to access the instance of the ViewModel class associated with the layout template. To change the
layout template for this particular action method, we called the setTemplate() method provided
by the ViewModel class.
In addition to the Layout controller plugin, there is the Layout view helper which
provides the same capabilities. With the Layout view helper, you can, for example,
switch layout from the static page which has no specific controller action.

6.4.1 Setting Layout for All Actions of a Controller


If all action methods of a controller class need to use the same alternative layout, you can override
the onDispatch() method of the AbstractActionController class and call the setTemplate()
method there, as shown in the example below:
// Add this alias in the beginning of the controller file
use Zend\Mvc\MvcEvent;
// ...
class IndexController extends AbstractActionController {
/**
* We override the parent class' onDispatch() method to
* set an alternative layout for all actions in this controller.
*/
public function onDispatch(MvcEvent $e) {
// Call the base class' onDispatch() first and grab the response
$response = parent::onDispatch($e);
// Set alternative layout

Page Appearance and Layout

156

$this->layout()->setTemplate('layout/layout2');
// Return the response
return $response;
}
}

6.5 Partial Views


A partial view is a .phtml view template file which can be rendered by another view template.
Partial views allow to compose your page of pieces and reuse pieces of view rendering logic
across different view templates.
For a simple example of partial view usage, lets imagine that we need to render a table of some
products. Each product has the ID, the name and the price. We can use partial view template to
render a single row of the table several times.
First, lets add the partialDemoAction() method to the Index controller:
// An action that demonstrates the usage of partial views.
public function partialDemoAction() {
$products = array(
array(
'id' => 1,
'name' => 'Digital Camera',
'price' => 99.95,
),
array(
'id' => 2,
'name' => 'Tripod',
'price' => 29.95,
),
array(
'id' => 3,
'name' => 'Camera Case',
'price' => 2.99,
),
array(
'id' => 4,
'name' => 'Batteries',
'price' => 39.99,
),
array(
'id' => 5,
'name' => 'Charger',
'price' => 29.99,

Page Appearance and Layout

157

),
);
return new ViewModel(array(
'products' => $products
));
}

The action method above just prepares an array of products for rendering and passes it to the
view template with the help of the ViewModel variable container.
Next, add the partial-demo.phtml template file:
1
2
3

<?php
$this->headTitle('Partial View Demo');
?>

4
5
6
7
8
9
10
11
12
13
14
15

<h1>Partial View Demo</h1>


<p>
Below, the table of products is presented. It is rendered with the help of
partial views.
</p>
<table class="table table-striped table-hover">
<tr>
<th>ID</th>
<th>Product</th>
<th>Price</th>
</tr>

16
17
18
19
20
21
22
23

<?php
foreach($this->products as $product) {
echo $this->partial('application/index/table-row',
array('product'=>$product));
}
?>
</table>

In the view template above, we define the markup for the table of products (lines 10-23). In line
18, we walk through the items of the products array and render each row with the Partial view
helper.
The first argument of the Partial view helper is the name of the partial view template file
(application/index/table-row).
The second argument of the Partial view helper should be an array of arguments passed to
the view template. They will be accessible the same way as if you would pass them with the
ViewModel variable container.
Finally, create the table-row.phtml view template, which will be used as the partial view template:

Page Appearance and Layout

158

<tr>
<td> <?php echo $this->product['id'] ?> </td>
<td> <?php echo $this->product['name'] ?> </td>
<td> <?php echo $this->product['price'] ?> </td>
</tr>

In the view template above, we just render a single row of the table.
To see the resulting web page, type http://localhost/application/index/partialdemo URL in your
browsers navigation bar. You should see something like in figure 6.5.

6.6 Placeholder View Helper


The Placeholder is another useful view helper allowing for capturing HTML content and storing
it for later use. Thus, analogous to the Partial view helper, it allows to compose your page of
several pieces.
For example, you can use the Placeholder view helper in pair with the Partial view helper
to decorate the content of a view template with another view template. A useful practical
application for this is layout inheritance.
Imagine the situation, when you need to create an alternative layout which has exactly the same
head section, header and the footer, but has differences in the middle page section. The rough
way for making such a layout would be to copy and paste the content of the original layout
template, and make necessary changes. Another (better) way is inheriting the original one,
when the resulting layout will reuse the common parts.
To demonstrate how to inherit a layout, we will create the layout2.phtml view template, which
will inherit the default layout.phtml template, and add the Ads bar at the right of the page.
Keeping ads in layout would be useful, if you plan to profit from displaying commercial ads on
all (or on most) of pages of your site.
The Placeholder view helper stores the data in PHP session storage. So, in theory, you can even capture content on one page and then
render/use it on another one.

159

Page Appearance and Layout

Figure 6.5. Table rows are rendered by partial views

Put the following code in the layout2.phtml template file:


1

<?php $this->placeholder('content')->captureStart(); ?>

2
3
4
5
6
7
8
9
10
11
12
13

<div class="row">
<div class="col-md-8">
<?php echo $this->content; ?>
</div>
<div class="col-md-4">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Ads</h3>
</div>
<div class="panel-body">
<strong>Zend Framework 2 Book</strong>

Page Appearance and Layout


14
15
16
17
18
19
20
21
22
23

160

<p>Learn how to create modern web applications with PHP


and Zend Framework 2</p>
<a target="_blank"
href="https://leanpub.com/using-zend-framework-2">
Learn More
</a>
</div>
</div>
</div>
</div>

24
25
26
27
28
29

<?php
$this->placeholder('content')->captureEnd();
echo $this->partial('layout/layout',
array('content'=>$this->placeholder('content')));
?>

In the code above, we call the captureStart() method (line 1) and captureEnd() method (line
15) of the Placeholder view helper to delimit the HTML markup that will be captured by the view
helper and stored in its internal storage (instead of rendering to PHP standard output stream).
In lines 3-14, we put the markup of the inherited layout. The derived layout uses the two-cell
grid. The first cell of the grid (spanning 8 columns) will contain the actual content of a certain
page, and the second cell (spanning 4 columns) will contain advertisements. For styling the ads,
we utilize the Panel interface component provided by the Twitter Bootstrap.
In line 16, we use the Partial view helper which is used to render the parent layout
(layout.phtml). We pass the content captured by the Placeholder view helper to the Partial
view helper as the second argument.
This way, we produced the nice-looking layout which inherits the default layout and improves
the code reusability.
Now, if you set the layout2.phtml for all actions of, say Index controller, you should be able to
see the result as in figure 6.6.

6.7 Adding Scripts to a Web Page


JavaScript code can be inserted into HTML pages and make them interactive. Scripts should be
inserted to an HTML file between <script> and </script> tags. Below, an example JavaScript
code is presented:

161

Page Appearance and Layout

<script type="text/javascript">
// Show a simple alert window with the "Hello World!" text.
$( document ).ready(function() {
alert('Hello World!);
});
</script>

In the example above, we created the <script> element, and put the jQuery callback function
in it. The jQuery binds a function to be executed when the DOM has finished loading. When
the function is executed, a simple alert window with the Hello World! text and OK button will
appear.

Figure 6.6. Inherited layout

Since you put this JavaScript code inside the HTML file, we will refer to it as inline script. An
alternative way of storing JavaScript code is putting it in an external .js file. External files
typically contain code that is designed to be used by several web pages. Typically, external
JavaScript files are stored in APP_DIR/public/js/ directory. To link an external JS file to your
HTML page, you add the <script> element like below:
<script type="text/javascript" src="/js/jquery.min.js"></script>

When the browser encounter such a <script> element, it reads the external JS file and executes
the code.
Generally, there are two places inside an HTML file where you can put the script:

162

Page Appearance and Layout

JavaScript code can be put in the <head> section of an HTML page. This method is
recommended to use when you need JavaScript to be loaded before the content of the
page. We used this method for loading the Twitter Bootstrap JavaScript extensions and
jQuery library.
Script can be placed at the bottom of the <body> section of an HTML page, just before the
closing </body> tag. This way is acceptable when you need the entire DOM to be loaded
before the script can start executing.
If a certain JavaScript file needs to be used on all (or on most) of the web pages, it is better to
place it in layout view template. But when a script needs to be used on a single page only, putting
it in the layout template is not the best idea. If you put such a script to layout template, the script
will be loaded to all pages, which can produce an unneeded traffic and increase page load time
for the whole site. To avoid this, you can add such a script for the desired page only.
To add a page-specific script which will be put in the <head> section of the web page, you use
the HeadScript view helper. Its methods are summarized by table 6.1:
Table 6.1. Methods provided by the HeadScript view helper

Method name

Description

appendFile()
offsetSetFile()
prependFile()
setFile()
appendScript()
offsetSetScript()
prependScript()
setScript()

Puts a link to external JS file after all others.


Inserts a link to external JS file in a given list position.
Puts a link to external JS file before all others.
Clears the list of scripts and puts the single external JS file in it.
Puts an inline script after all others.
Inserts an inline script to a given list position.
Puts an inline script before all others.
Clears the list of inline scripts and puts the single inline
script in it.

To add a link to external JS file to the <head> section, of a page, you add the following PHP code
in the beginning of your view template (.phtml) file:
<?php
$this->headScript()->appendFile('/js/yourscript.js', 'text/javascript');

In the code above, we called the appendFile() method of the HeadScript view helper. This
method takes two arguments. The first one is the path to external JS file (if the file is stored
inside of APP_DIR/public/js directory, or an URL of a JS file if the file is located on another web
server). The second argument is the type of the script (it is typically equal to text/javascript).
Other methods provided by HeadScript view helper (such as prependFile(), offsetSetFile()
and setFile() differentiate only in the position in the list of scripts into which the new script
will be inserted.
The DOM (Document Object Model) is a convenient representation of an HTML document structure as a tree of elements.

Page Appearance and Layout

163

The methods prependScript(), appendScript(), offsetSetScript() and setScript() are


designed to insert an inline JavaScript code. They are rarely used, because you typically insert
external JS scripts in the head section of the document
To insert a script to the end of the <body> section of the document, you can use the InlineScript
view helper . It provides exactly the same methods as the HeadScript view helper. Below,
an example is presented which can be used to append an inline JavaScript code to the end of
document body:
<?php
$script = <<<EOT
$( document ).ready(function() {
alert('Hello World!);
});
EOT
$this->inlineScript()->appendScript($script);

In the example above, we used the PHPs Heredoc syntax to fill in the $script variable with
the inline JavaScript code. Then we call the appendScript() function on the InlineScript view
helper and pass the code as its argument.
But, using the InlineScript view helper may be not very convenient in sense of readability.
Moreover, NetBeans IDE syntax checker will be stuck on the Heredoc notation and will not
recognize the JavaScript code. To fix this, you can simply put the <script> element at the bottom
of your view template, as shown in the example below:
<!-- Page content goes first -->
<!-- Inline script goes last -->
<script type="text/javascript">
$( document ).ready(function() {
// Show a simple alert window with the "Hello World!" text.
alert("Hello World!");
});
</script>

This ensures the same effect is achieved as with InlineScript view helper, but allows for better
script readability and automatic syntax checking.
For HeadScript and InlineScript view helpers to work, you should ensure their
content is echoed in layout view template (look at lines 27 and 72 of layout.phtml file).
If you remove those lines from the layout template, the scripts wont be inserted in the
web page.

The name InlineScript does not fully reflect the capabilities of this view helper. Actually, it can insert both inline and external scripts.
The better name for this view helper would be BodyScript, because it is intended for inserting scripts in document body.
Heredoc is an alternative string definition method provided by PHP. It works well with multi-line strings.

Page Appearance and Layout

164

6.7.1 Example
For a real-life example of inserting a JavaScript code in your web page, lets add a page with autocomplete feature. With this feature, the web browser will predict a word or phrase that the user
wants to type in by several first letters, without the user actually entering the text completely. We
can use an auxiliary JavaScript library called Twitter Typeahead. Analogous to Twitter Bootstrap,
the Typeahead library was developed in Twitter Inc. for their internal purposes and is distributed
freely.
Download typeahead.min.js file (a minified version of the Typeahead library) from the official
project page. When the download is finished, place the file in your APP_DIR/public/js directory.
Then add the typeahead.phtml file in your application/index/static subdirectory under the
modules view directory. This directory is served by the StaticRoute route type that weve
created and configured earlier in Chapter 5, and all static pages placed here will automatically
become available to site users.
In the typeahead.phtml view template file, put the following content:
1
2
3
4
5

<?php
$this->headTitle('Typeahead');
// Add a JavaScript file
$this->headScript()->appendFile('/js/typeahead.min.js', 'text/javascript');
?>

6
7
8
9

<h1>Typeahead</h1>
<p>Type a continent name (e.g. Africa) in the text field below:</p>
<input type="text" class="typeahead" title="Type here"/>

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

<script type="text/javascript">
$( document ).ready(function() {
$('input.typeahead').typeahead({
name: 'continents',
local: [
'Africa',
'Antarctica',
'Asia',
'Europe',
'South America',
'North America'
]
});
});
</script>

In the code above, we set the title for the page (line 2), then we append the typeahead.min.js file
to the <head> section of the page with the HeadScript view helper (line 4).
http://twitter.github.io/typeahead.js/

Page Appearance and Layout

165

In line 9, we create a text input field where the user will be able to enter some text. We mark the
input field with the typeahead CSS class.
Lines 11-25 contain inline JavaScript code placed at the bottom of the view template (we dont
use InlineScript view helper for better code readability).
In line 12, we have the jQuery event handler bound to the document is ready event. This event
is fired when the complete DOM tree has been loaded.
In line 13, we have the jQuery selector (input.typeahead) which selects all input fields marked
with the typeahead CSS class and execute the typeahead() function on them.
The typeahead() function binds the change event handler to the text input field. Once the user
enters a character in the field, the handler is executed and checks the letters entered. It then
displays the dropdown menu with suggested auto-completion variants.
The typeahead() function takes two arguments: the name argument identifies the dataset, and
the local argument is a JSON array containing the available auto-completion variants.
To give the auto-completion field and its dropdown menu a nice-looking visual appearance, add
the following CSS rules to your style.css file.
.typeahead,
.tt-query,
.tt-hint {
width: 396px;
height: 30px;
padding: 0px 12px;
font-size: 1.1em;
border: 2px solid #ccc;
border-radius: 4px;
outline: none;
}
.tt-dropdown-menu {
width: 422px;
margin-top: 12px;
padding: 8px 0;
background-color: #fff;
border: 1px solid #ccc;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 4px;
}
.tt-suggestion {
padding: 3px 20px;
font-size: 1.1em;
line-height: 24px;
}

166

Page Appearance and Layout

.tt-suggestion.tt-is-under-cursor {
color: #fff;
background-color: #0097cf;
}
.tt-suggestion p {
margin: 0;
}

To see the auto-completion feature in work, type the http://localhost/typeahead URL in your
browser and press Enter. The Typeahead page will appear with the prompt to enter a continent
name. For example, type a letter to see how Typeahead suggests you available variants (figure
6.7).

Figure 6.7. Auto-complete feature

Page Appearance and Layout

167

You can see this example working in the Hello World sample bundled with this book
by typing the URL http://localhost/typeahead in your browser.

6.8 Adding CSS Stylesheets to a Web Page


CSS stylesheets are typically placed to the <head> section of an HTML document, either as a
link to an external file (external CSS stylesheet files are usually stored in APP_DIR/public/css
directory.)
<link rel="stylesheet" type="text/css" href="/css/style.css">

or as an inline <style> element


<style>
body {
padding-top: 60px;
padding-bottom: 40px;
}
</style>

External CSS stylesheets are recommended to store the CSS rules. For example, the base CSS rules
provided by Twitter Bootstrap CSS framework are loaded from bootstrap.min.css and bootstraptheme.min.css files. Custom site-specific CSS rules can be stored in style.css file. Since you need
this CSS stylesheets for most of your pages, it is better to link them in the head section of the
layout template. But, if a certain CSS stylesheet needs to be loaded for a single page only, you
place it on that pages view template.
To add an external CSS stylesheet to a view template, you use the HeadLink view helper:
1
2
3
4

<?php
$this->headLink()->appendStylesheet('/css/style.css');
$this->headLink()->appendStylesheet(
'http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css');

In the example code above, we used the appendStylesheet method of the HeadLink view helper
to add an external CSS stylesheet to the head section of the document. The method accepts a
path to local CSS file (line 2) or a URL to CSS file located on another server (line 3).
The summary of HeadLink view helpers methods is provided in table 6.2).

168

Page Appearance and Layout

Table 6.2. Methods provided by HeadLink view helper

Method name

Description

appendStylesheet()
offsetSetStylesheet()
prependStylesheet()
setStylesheet()

Puts a link to CSS stylesheet file after all others.


Inserts a link to CSS stylesheet file in a given list position.
Puts a link to external CSS stylesheet file before all others.
Clears the list and puts the single CSS file instead.

If you want to add an inline <style> element in the head section of the document, you can use
the HeadStyle view helper. Its methods are presented in table 6.3 below:
Table 6.3. Methods of the HeadStyle view helper

Method name

Description

appendStyles()
offsetSetStyle()
prependStyle()
setStyle()

Adds an inline CSS code after all others.


Inserts an inline CSS stylesheet file in a given list position.
Puts a link to external CSS stylesheet file before all others.
Clears the list and puts the single CSS file instead.

6.8.1 Example
To demonstrate how to add a CSS stylesheet to your web page, we will take a real-life example.
Assume you need to let the user the ability to type a date (in YYYY-MM-DD format) in a text
input field. You would like to improve user experience by not just letting him to type the date,
but also by selecting it from a pop-up date-picker widget.
To achieve this goal, you can use a third-party library called jQuery UI . To integrate jQuery
UI in your page, you need to download two files from the official project page:
jquery-ui.min.js the minified version of jQuery UI JavaScript code;
jquery-ui.min.css the minified version of jQuery UI theming styles.
Put the jquery-ui.min.js file to APP_DIR/public/js, and jquery-ui.min.css file to APP_DIR/public/css. Finally, add the datepicker.phtml view template:

jQuery UI provides a set of user interface interactions, effects, widgets, and themes; it is based on jQuery library. jQuery UI is analogous
to Twitter Bootstrap in the sense that both provide reusable user interface components.
http://jqueryui.com/

Page Appearance and Layout


1
2

169

<?php
$this->headTitle('Datepicker');

3
4
5
6

$this->headScript()->appendFile('/js/jquery-ui.min.js', 'text/javascript');
$this->headLink()->appendStylesheet('/css/jquery-ui.min.css');
?>

7
8

<h1>Datepicker</h1>

9
10

<p>

11

Click the edit box below to show the datepicker.


</p>

12
13
14

<input type="text" class="datepicker" title="Type here"/>

15
16
17
18
19
20

<script>
$( document ).ready(function() {
$( "input.datepicker" ).datepicker({ dateFormat: 'yy-mm-dd' });
});
</script>

In the example above, we use the HeadScript view helpers appendFile() method (line 4) to add
the link to jquery-ui.min.js file to the head section of the document.
In line 5, we used the HeadLink view helpers appendStylesheet() method to add the link to
jquery-ui.min.css CSS stylesheet to the head section of the document.
In line 8, we added the text input field which will be used to enter the date.
In line 10-14, we added an inline JavaScript code for binding jQuery event handler to the text
input field. When the user clicks the text input field, the datepicker widget will appear allowing
to select the date.
To see the result, enter the http://localhost/datepicker URL into your browsers navigation bar
(see figure 6.8 for example).

6.9 Writing Own View Helpers


Earlier in this chapter, weve created the layout common to all pages of the web site. But we still
have a couple of things to do to make the layout fully functional. If you remember, the layout
template contains the navigation bar and breadcrumbs. But both navigation bar and breadcrumbs
interface components provided by Twitter Bootstrap are currently static, while they need to be
more interactive.
For example, the active item of the navigation bar should depend on the controllers action that
is being executed at the moment. And the breadcrumbs should display the path to the currently
viewed page. In this section we will make these widgets completely ready for the web site with
the help of our own view helpers.

170

Page Appearance and Layout

Figure 6.8. Datepicker

A typical view helper is a PHP class deriving from Zend\View\Helper\AbstractHelper base


class, which in turn implements the Zend\View\Helper\HelperInterface interface (class inheritance diagram is presented in figure 6.9).

Figure 6.9. View helper class diagram

171

Page Appearance and Layout

6.9.1 Menu
First, lets implement the Menu view helper class that will render the HTML code of the navigation
bar. The Menu class will provide several methods allowing to set menu items in a form of array,
set the active menu item and render the menu (see table 6.4 for method summary).
Table 6.4. Methods of the Menu view helper

Method name

Description

__construct($items)
setItems($items)
setActiveItemId($activeItemId)
render()
renderItem($item)

Class constructor.
Method for setting the menu items.
Method for setting the currently active menu item.
Renders the menu.
Renders a single menu item.

The information describing a single menu item will be represented by an array like below (for
example, the Home item will have an ID, text label and an URL for a hyperlink):
array(
'id' => 'home',
'label' => 'Home',
'link' => $this->url('home')
)

We also want to add the support for dropdown menus as navigation items. For example, in case of
the Support dropdown menu having the Documentation and Help sub-items, the item description
will take the following form:
array(
'id' => 'support',
'label' => 'Support',
'dropdown' => array(
array(
'id' => 'documentation',
'label' => 'Documentation',
'link' => $this->url('doc', array('page'=>'contents'))
),
array(
'id' => 'help',
'label' => 'Help',
'link' => $this->url('static', array('page'=>'help'))
)
)
)

172

Page Appearance and Layout

We want to put the Menu class in Application\View\Helper namespace. Thus, start from creating
the Menu.php file in the View/Helper directory under the Application modules source directory
(figure 6.10).

Figure 6.10. View helper directory

Why do we place the view helper class under modules source directory?
View helpers (unlike .phtml view templates) are stored under modules src/ directory,
because they are usual PHP classes and require to be resolved by a PHP class autoloading feature. On the other hand, view templates are resolved by the special ZF2 class
called view resolver, and for this reason, view templates are stored under the modules
view/ directory.

Next, create the stub code for the Menu class:


1
2

<?php
namespace Application\View\Helper;

3
4

use Zend\View\AbstractHelper;

5
6
7

// This view helper class displays a menu bar.


class Menu extends \Zend\View\Helper\AbstractHelper {

8
9
10

// Menu items array.


protected $items = array();

11
12
13

// Active item's ID.


protected $activeItemId = '';

14
15
16

// Constructor.
public function __construct($items=array()) {

Page Appearance and Layout

173

$this->items = $items;

17

18
19

// Sets menu items.


public function setItems($items) {
$this->items = $items;
}

20
21
22
23
24

// Sets ID of the active items.


public function setActiveItemId($activeItemId) {
$this->activeItemId = $activeItemId;
}

25
26
27
28
29
30

In the code above, we defined several private fields for the Menu class. The $items field (line 10)
is an array which will store the information on the menu items; and the $activeItemId field
(line 13) is the ID of an active menu item. The active menu item will be visually highlighted.
In lines 15-18, we defined the class constructor method, which (optionally) takes the array of
items for initializing the menu. An alternative method of menu initialization is through the
setItems() method (lines 20-23). And the setActiveItemId() method (lines 25-28) sets the ID
of the currently active menu item.
Next, lets add the render() method, which will generate HTML code for the whole navigation
bar and return it as a text string:
1
2

// Renders the menu.


public function render() {

3
4
5

if(count($this->items)==0)
return ''; // Do nothing if there are no items.

6
7
8
9
10
11
12
13
14
15
16

$result
$result
$result
$result
$result
$result
$result
$result
$result
$result

= '<nav class="navbar navbar-default" role="navigation">';


.= '<div class="navbar-header">';
.= '<button type="button" class="navbar-toggle" ';
.= 'data-toggle="collapse" data-target=".navbar-ex1-collapse">';
.= '<span class="sr-only">Toggle navigation</span>';
.= '<span class="icon-bar"></span>';
.= '<span class="icon-bar"></span>';
.= '<span class="icon-bar"></span>';
.= '</button>';
.= '</div>';

17
18
19
20

$result .= '<div class="collapse navbar-collapse navbar-ex1-collapse">';


$result .= '<ul class="nav navbar-nav">';

Page Appearance and Layout

174

// Render items
foreach($this->items as $item) {
$result .= $this->renderItem($item);
}

21
22
23
24
25

$result .= '</ul>';
$result .= '</div>';
$result .= '</nav>';

26
27
28
29

return $result;

30
31

In the code above, we produce the HTML markup for the Bootstrap navbar component. The
navbar will use the default theme and will be collapsible (adaptive to different screen widths).
The navbar will not have the brand text in the header. In lines 24-26, we loop through the menu
items and render each one with the renderItem() method. Finally, the render() method returns
the resulting HTML code as a text string.
To finish with creating the Menu class, lets implement the renderItem() method. This method
will produce the HTML code for a single menu item:
1
2

// Renders an item.
protected function renderItem($item) {

3
4
5
6

$id = isset($item['id']) ? $item['id'] : '';


$isActive = ($id==$this->activeItemId);
$label = isset($item['label']) ? $item['label'] : '';

7
8

$result = '';

9
10

if(isset($item['dropdown'])) {

11
12

$dropdownItems = $item['dropdown'];

13
14
15
16

$result .= '<li class="dropdown ' . ($isActive?'active':'') . '">';


$result .= '<a href="#" class="dropdown-toggle" data-toggle="dropdown">';
$result .= $label . ' <b class="caret"></b>';

17
18

$result .= '<ul class="dropdown-menu">';

19
20
21
22

foreach($dropdownItems as $item) {
$link = isset($item['link']) ? $item['link'] : '#';
$label = isset($item['label']) ? $item['label'] : '';

23
24
25

$result .= '<li>';
$result .= '<a href="'.$link.'">'.$label.'</a>';

Page Appearance and Layout

175

$result .= '</li>';

26

27
28

$result .= '</ul>';
$result .= '</a>';
$result .= '</li>';

29
30
31
32

} else {
$link = isset($item['link']) ? $item['link'] : '#';

33
34
35

$result .= $isActive?'<li class="active">':'<li>';


$result .= '<a href="'.$link.'">'.$label.'</a>';
$result .= '</li>';

36
37
38

39
40

return $result;

41
42

In the renderItem() methods code above we did the following. First, we checked whether the
item is a dropdown menu or a simple item (line 10). If the item is a dropdown menu, we walk
through the dropdown menu items, and render each one in turn (lines 15-20). Lines 25-30 contain
the rendering code for the case of a simple item.
To be able to use the Menu view helper in a view template, it is required to register it in
configuration. To do that, add the following view_helpers key in the module.config.php file:
<?php
return array(
// ...
// The following registers our custom view
// helper classes in view plugin manager.
'view_helpers' => array(
'invokables' => array(
'mainMenu' => 'Application\View\Helper\Menu',
),
),
);

In the example above, we registered our Menu class as a mainMenu view helper and will be able
to access it from any view template.
Since we plan to use the Menu view helper in the layout view template, replace the navigation
menu markup in layout.phtml file with the following code:

Page Appearance and Layout


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

176

<!-- Navigation bar -->


<?php
$this->mainMenu()->setItems(array(
array(
'id' => 'home',
'label' => 'Home',
'link' => $this->url('home')
),
array(
'id' => 'downloads',
'label' => 'Downloads',
'link' => $this->url("application/default",
array('controller'=>'index', 'action'=>'downloads'))
),
array(
'id' => 'support',
'label' => 'Support',
'dropdown' => array(
array(
'id' => 'documentation',
'label' => 'Documentation',
'link' => $this->url('doc', array('page'=>'contents'))
),
array(
'id' => 'help',
'label' => 'Help',
'link' => $this->url('static', array('page'=>'help'))
)
)
),
array(
'id' => 'about',
'label' => 'About',
'link' => $this->url('about')
),
));

37
38
39

echo $this->mainMenu()->render();
?>

In the code above, we access the registered mainMenu view helper and set the navigation bar
items with the help of setItems() method (line 1). As a parameter for the method, we pass the
array of items. Then we render the navigation bar with the render() method.
To set the active item for the navigation bar, we can call the setActiveItemId() method from
any view template. For example, add the following code to the beginning of the view template
for the About page (application/index/about.phtml) as follows:

Page Appearance and Layout

177

<?php
$this->mainMenu()->setActiveItemId('about');
?>

Now, if you open the About page in your browser, you should see that the About item of the
navigation menu is highlighted with a different color. To display the active item properly, you
need to call the setActiveItemId() method for each page presenting in the navbar (Home,
Downloads, Documentation, etc.) You can see how this is done in the Hello World sample.

6.9.2 Breadcrumbs
Now that you know how to implement a view helper, lets create the second view helper for
rendering the breadcrumbs. It is completely analogous to the Menu view helper, so below we just
provide the complete code of the Breadcrumbs class:
1
2

<?php
namespace Application\View\Helper;

3
4

use Zend\View\AbstractHelper;

5
6
7

// This view helper class displays breadcrumbs.


class Breadcrumbs extends \Zend\View\Helper\AbstractHelper {

8
9
10

// Array of items.
private $items = array();

11
12
13
14
15

// Constructor.
public function __construct($items=array()) {
$this->items = $items;
}

16
17
18
19
20

// Sets the items.


public function setItems($items) {
$this->items = $items;
}

21
22
23

// Renders the breadcrumbs.


public function render() {

24
25
26

if(count($this->items)==0)
return ''; // Do nothing if there are no items.

27
28
29
30

// Resulting HTML code will be stored in this var


$result = '<ol class="breadcrumb">';

Page Appearance and Layout

178

// Get item count


$itemCount = count($this->items);

31
32
33

$itemNum = 1; // item counter

34
35

// Walk through items


foreach($this->items as $label=>$link) {

36
37
38

// Make the last item inactive


$isActive = ($itemNum==$itemCount?true:false);

39
40
41

// Render current item


$result .= $this->renderItem($label, $link, $isActive);

42
43
44

// Increment item counter


$itemNum++;

45
46

47
48

$result .= '</ol>';

49
50

return $result;

51

52
53

// Renders an item.
protected function renderItem($label, $link, $isActive) {

54
55
56

$result = $isActive?'<li class="active">':'<li>';

57
58

if(!$isActive)
$result .= '<a href="'.$link.'">'.$label.'</a>';
else
$result .= $label;

59
60
61
62
63

$result .= '</li>';

64
65

return $result;

66

67
68

To be able to use the Breadcrumbs view helper, register it in the module.config.php file as follows:

179

Page Appearance and Layout


1
2

<?php
return array(

3
4

//...

5
6
7
8
9
10
11

// The following registers our custom view helper classes.


'view_helpers' => array(
'invokables' => array(
'pageBreadcrumbs' => 'Application\View\Helper\Breadcrumbs',
),
),

12
13

);

Since we plan to use the Breadcrumbs view helper in the layout view template, replace the
breadcrumbs markup in layout.phtml file with the following code:
<!-- Breadcrumbs -->
<?php echo $this->pageBreadcrumbs()->render(); ?>

In the code above, we access the pageBreadcrumbs() view helper and call it with the render()
method. The echo operator then outputs the HTML code of the breadcrumbs.
Finally, you need to pass the breadcrumbs items for each view template. For example, add the
following lines in the view template for the About page:
<?php
$this->pageBreadcrumbs()->setItems(array(
'Home'=>$this->url('home'),
'About'=>$this->url('about'),
));
?>

Now, if you open the about page, you should see breadcrumbs as in figure 6.11 below. Site users
will easily see what page they are visiting right now and will not get lost.

Figure 6.11. Breadcrumbs for the About page

6.10 View Models and Page Composition


Earlier, when we wrote action methods for the controller classes, we used the ViewModel class
as a variable container for passing the variables from controller to view template, and we also
used the ViewModel for overriding the default view template name.

Page Appearance and Layout

180

But, actually the ViewModel class is more than just a variable container plus view template name.
In fact, it is closely related to the layout and page composition.
The third big capability of the view model class is that it allows for combining several view
models in a tree-like structure. Each view model in the tree has the associated view template
name and data variables that can be passed to the view template to control the process of
rendering.
This feature is internally used by Zend Framework 2 when combining the layout view template
and the view template associated with the controllers action method. ZF2 internally creates the
view model for the layout template and assigns it with layout/layout view template name.
When your controllers action method returns the ViewModel object, this object is attached as a
child to the layout view model (see figure 6.12 for an example).

Figure 6.12. View models nested in a tree-like structure

The resulting process of page rendering is the following:


The child view model is visited first and its associated view template is rendered, and the
resulting HTML markup is saved in a temporary storage;
The output HTML markup of the child view model is passed to the layout view model as
the $content variable. This way the layout view template can render the content specific
to the certain page.
Table 6.5 gives the summary of the methods provided by the ViewModel class for the purpose of
page composition:

181

Page Appearance and Layout

Table 6.5. Methods of the ViewModel class for page composition

Method name

Description

addChild()
getChildren()
hasChildren()
clearChildren()
count()
getIterator()
setTerminal()
terminate()
setCaptureTo()
setAppend()
isAppend()

Adds a child view model.


Gets the list of child view models.
Tests if the view model has children or not.
Removes all child view models.
Returns count of child view models.
Returns the iterator for child view models.
Sets the terminal flag.
Tests whether the view model is terminal.
Sets the name of the variable for capturing the output.
Sets the append flag.
Tests whether to append this view model to another one.

Below, we provide the brief description of the methods presented in the table above.
The addChild(), getChild(), hasChildren() and clearChildren() methods are used for
(respectively) adding a child view model to the parent one, retrieving the array of view models
attached, testing if the view model is leaf (doesnt have children) and detaching all children.
The setCaptureTo() method allows to set the variable in the parent view template into which to
inject the HTML markup code produced by a child view template. If two child view models use
the same variable, the second one will overwrite the first one. The setAppend() method can be
used when you need to inject the results of two or more view templates into a single placeholder
variable. The next rendered view template will be appended to the variables existing content.
The view model returned by the controller is assigned the $content capture variable.
A view model can be marked as terminal with the setTerminal() method. The setTerminal()
method takes a single flag parameter. If true, the view model is considered as terminal (top-level
parent) and the renderer returns the output of the view template to the application, otherwise its
parents are rendered as well. The method terminate() tests whether the view model is terminal
or not.
The setTerminal() method is very useful in some situations, because with its help you
can disable the rendering of the layout view template. If you return from controller the
view model marked as terminal, the layout will not be applied. This can be used, for
example, when you want to load part of a page asynchronously by an AJAX request
and need to insert its HTML code in the DOM tree of an existing page.

6.11 Summary
Zend Framework 2 is shipped with Twitter Bootstrap that is a CSS framework allowing for
creating visual appealing and professionally looking web applications. It provides the base CSS
AJAX (stands for Asynchronous JavaScript and XML) is a capability provided by modern browsers which can be used to send data to, and
retrieve data from, a server asynchronously (in background) without interfering with the display and behavior of the existing page.

Page Appearance and Layout

182

rules, the simple layout grid, and useful interface components (like navigation bars, breadcrumbs,
pagination, etc.)
In a typical web site, pages have common structure (for example, a typical page may have
a navigation bar at the top, the body with page content, and the footer with the copyright
information at the bottom). In Zend Framework 2, you define this common structure with a
view template file called the layout. The layout template may have placeholder(s) in which ZF2
puts the content specific to a particular web page.
View helpers are (relatively) simple PHP classes that encapsulate a part of page rendering work.
For example, they allow for composing the page of several parts, setting page title and meta tags,
and creating the reusable widgets like navigation bar or breadcrumbs.

7 Collecting User Input with Forms


In this chapter, you will become familiar with using web forms for gathering data entered by site
users. In Zend Framework 2, functionality for working with forms is mainly spread across four
components: the Zend\Form component, which allows you to build forms and contains the view
helpers for rendering form elements; the Zend\Filter, Zend\Validator and Zend\InputFilter
components which allow you to filter and validate user input:
Component

Description

Zend\Form

Contains base form model classes.

Zend\Filter

Contains various filters classes.

Zend\Validator

Implements various validator classes.

Zend\InputFilter

Implements a container for filters/validators.

7.1 Get the Form Demo Sample from GitHub


We will demonstrate form usage on the Form Demo sample web application bundled with the
book. This sample is a complete web site you can install and see the working forms in action.
To download the Form Demo application, visit this page and click the Download ZIP button to
download the code as a ZIP archive. When the download is complete, unpack the archive to a
directory of your choosing.
Then navigate to the formdemo directory which contains the complete source code of the Form
Demo web application:
/using-zend-framework-2-book
/formdemo
...

To install the example, you can either edit your default virtual host file or create a new
one. After editing the file, restart the Apache HTTP Server and open the web site in
your web browser. For additional information on Apache virtual hosts, you can refer
to Appendix A.

https://github.com/olegkrivtsov/using-zend-framework-2-book

184

Collecting User Input with Forms

7.2 About HTML Forms


Form functionality provided by Zend Framework 2 internally uses HTML forms. Because of that,
we start with a brief introduction to HTML forms topic.
In HTML, forms are enclosed with <form> and </form> tags. A form typically consists of fields:
text input fields, check boxes, radio buttons, submit buttons, hidden fields and so on. HTML
provides several tags intended for defining form fields:
<input> - specifies an input field where the user can enter some data (field appearance
and behavior depends on the field type);
<textarea> - multi-line text area which can contain an unlimited number of characters;
<button> - a clickable button;
<select> - a dropdown list;
<option> - used inside the <select> element for defining the available options in a
dropdown list.
In table 7.1, you can find examples of HTML form field definitions. Figure 7.1 contains corresponding field visualizations (except the hidden field type, which has no visual representation).

Figure 7.1. Standard HTML form fields

The <button> field is analogous to <input type="button">, however it provides additional capabilities, like specifying a graphical icon
on the button.

185

Collecting User Input with Forms

Table 7.1. Standard HTML form fields

Field

Definition

Text input field

<input type="text" />

Text area

<textarea rows=4></textarea>

Password

<input type="password" />

Button

<input type="button" value="Apply"/> or


<button type="button">Apply</button>

Submit button

<input type="submit" value="Submit" />

Image (graphical submit button)

<input type="image" src="button.jpg" />

Reset button

<input type="reset" value="Reset"/>

Checkbox

<input type="checkbox">Remember me</input>

Radio

<input type="radio" value="Radio">Allow</input>

Select

<select><option>Enable</option><option>Disable</option></select>

File

<input type="file" />

Hidden field

<input type="hidden" />

HTML5 introduced several new form field types (listed in table 7.2); figure 7.2 contains
corresponding field visualizations.
HTML5 fields provide more convenient ways for entering the most frequently used data types:
numbers, dates, E-mails, URLs, etc. Additionally, on form submit, the web browser validates that
the user entered data is in a correct format, and if not the browser will prevent form submission
and ask the user to correct the input error.
Table 7.2. HTML5 form fields

Field

Definition

Color picker
Date
Date-time (with time zone)
Date-time (without time zone)
E-mail address
Number
Time
Month
Week
URL
Range (slider)
Search field
Telephone number

<input
<input
<input
<input
<input
<input
<input
<input
<input
<input
<input
<input
<input

type="color" />
type="date" />
type="datetime" />
type="datetime-local" />
type="email" />
type="number" />
type="time" />
type="month" />
type="week" />
type="url" />
type="range" />
type="search" name="googlesearch" />
type="tel" />

186

Collecting User Input with Forms

At the moment of writing this chapter, not all modern web browsers completely support
HTML5 form fields.

7.2.1 Fieldsets
You can group related form fields with the help of the <fieldset> tag, as shown in the example
below. The optional <legend> tag allows you to define the caption for the group.
<fieldset>
<legend>Choose a payment method:</legend>
<input type="radio" name="payment" value="paypal">PayPal</input>
<input type="radio" name="payment" value="card">Credit Card</input>
</fieldset>

Figure 7.2. HTML5 form fields

The HTML markup presented above will generate the group as in figure 7.3:

Figure 7.3. Fieldset

Collecting User Input with Forms

187

7.2.2 Example: Contact Us Form


An example of a typical HTML form is presented below:
1
2
3
4
5
6
7
8
9
10
11
12

<form name="contact-form" action="/contactus" method="post">


<label for="email">E-mail</label>
<input name="email" type="text">
<br>
<label for="subject">Subject</label>
<input name="subject" type="text">
<br>
<label for="body">Message</label>
<textarea name="body" class="form-control" rows="6"></textarea>
<br>
<input name="submit" type="submit" value="Submit">
</form>

In the example above, we have the feedback form which allows the user to enter his E-mail
address, message subject, text, and then submit them to the server. The form definition begins
with the <form> tag (line 1).
The <form> tag contains several important attributes:
the name attribute specifies the name of the form (contact-form).
the action attribute defines the URL of the server-side script which is responsible for
processing the submitted form (/contactus).
the method attribute defines the method (either GET or POST) to use for delivering form
data. In this example, we use the POST method (recommended).
In line 3, we define a text input field with the help of the <input> element. The name attribute
specifies the name of the field (email). The type attribute specifies the purpose of the element
(the type text means the input field is intended for entering text).
In line 2, we have the <label> element which represents the label for the E-mail text input field
(the corresponding input fields name is determined by the for attribute of the <label> element).
In lines 5-6, by analogy, we have the Subject input field and its label.
In line 9, we have the text area field which is suited well for entering multi-line text. The height
of the text area (6 rows) is defined by the rows attribute.
In line 11, we have the submit button (input element with submit type). The value attribute
allows you to set the title text for the button (Submit). By clicking this button, the user will
send the form data to the server.
Line break <br> elements are used in lines 4, 7 and 10 to position form controls one below another
(otherwise they would be positioned in one line).
To see what this form looks like, you can put its HTML markup code in a .html file and open
the file in your browser. You will see the form visualization as in figure 7.4.

Collecting User Input with Forms

188

Figure 7.4. Visualization of the feedback form

If you enter some data in the feedback form and click the Submit button, the web browser will
send an HTTP request to the URL you specified in the action attribute of the form. The HTTP
request will contain the data you entered.

7.2.3 GET and POST Methods


HTML forms support GET and POST methods for submitting the data to server. These methods
have important technical differences.
When using POST method (the default) for submitting the form, the data is sent in HTTP request
body. For example, when you press the Submit button on the feedback form, an HTTP request
will look like the example below:
1
2
3
4
5
6
7
8

POST http://localhost/contactus HTTP/1.1


Host: localhost
Connection: keep-alive
Content-Length: 76
Accept: text/html,application/xhtml+xml,application/xml
Origin: null
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64)
Content-Type: application/x-www-form-urlencoded

9
10

email=name%40example.com&subject=Example+Subject&body=Hello%21&submit=Submit

Above, you can see that the form data is transmitted in request body (line 10). Form fields are
concatenated in a single string and then URL-encoded to replace unsafe characters with allowed
characters from the ASCII table.
In comparison, when you set the GET method for the form, an HTTP request will look like the
example below:

Collecting User Input with Forms


1
2
3
4
5
6
7

189

GET http://localhost/contactus?email=name%40example.com&subject=Example+Subjec\
t&body=Hello%21&submit=Submit HTTP/1.1
Host: localhost
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64)
Accept-Encoding: gzip,deflate,sdch

In the example above, you can see that the form data is concatenated, URL-encoded and sent as
part of the HTTP requests URL (line 1), which makes the URL long and harder to read. Since
the form data is sent inside the URL, this makes it easily visible to site visitors.
In most cases, you will use the POST method for delivering form data in the request body, because
the user doesnt need to see the data in the browsers navigation bar (especially when submitting
passwords or other sensitive data).
Please note that submitting form data using the POST method does not protect your
sensitive data (like passwords, credit card numbers, etc.) from being stolen. To protect
such data, youll have to direct your HTTP traffic to a SSL tunnel (SSL stands for
Secure Sockets Layer). Protected SSL connections are distinguished by using the https://
schema in web page URLs. To enable SSL for your Apache HTTP server, you will need
to obtain an SSL certificate from a trusted provider (like VeriSign) and install it on
your server.

7.3 Styling HTML Forms with Twitter Bootstrap


In ZF2-based web sites, we use the Twitter Bootstrap CSS Framework that provides default CSS
rules for styling forms and form fields. To apply the CSS rules to a form field (like <input>,
<textarea>, etc.), you should assign it the .form-control CSS class. Additionally, when using
labels together with input fields, put the label-input pairs inside of <div> elements with the
.form-group CSS class. For submit buttons, you can use the .btn CSS class plus a theme class
like .btn-default, .btn-primary, etc.
Below, we provide the modified example of the feedback form which uses the Bootstrap styling:

http://en.wikipedia.org/wiki/Secure_Sockets_Layer
http://www.verisign.com/

Collecting User Input with Forms

190

<h1>Contact Us</h1>
<p>
Please fill out the following form to contact us.
We appreciate your feedback.
</p>
<form name="contact-form" action="/contactus" method="post">
<div class="form-group">
<label for="email">Your E-mail</label>
<input name="email" type="text" class="form-control"
placeholder="name@example.com">
</div>
<div class="form-group">
<label for="subject">Subject</label>
<input name="subject" type="text" class="form-control"
placeholder="Type subject here">
</div>
<div class="form-group">
<label for="body">Message Body</label>
<textarea name="body" class="form-control" rows="6"
placeholder="Type message text here"></textarea>
</div>
<input name="submit" type="submit"
class="btn btn-primary" value="Submit">
</form>

The visualization of the form is presented in figure 7.5.


Because Twitter Bootstrap is designed to support mobile phones, tablets, and desktops, it makes
the form fields as wide as the size of the screen. This may make your form too wide and hard to
understand. To limit form width, you can use the Bootstrap-provided grid, like in the example
below:
<div class="row">
<div class="col-md-6">
<form>
...
</form>
</div>
</div>

191

Collecting User Input with Forms

In the HTML markup above, we put a form inside of the 6-column-width grid cell, which makes
the form half the width of the screen.

Figure 7.5. Styled feedback form

7.4 Retrieving Form Data in a Controllers Action


The site user typically works with the form in the following order:
First, a controllers action is executed rendering the web page containing the form
prompting the site user for input. Once the user fills the form fields, they click the Submit
button, and this generates a HTTP request and sends the data to the server.
Second, in your controllers action method, you can extract the submitted data from POST
(and/or GET) variables, and display the page with the results of the form processing.
Typically these two web pages are handled by the same controller action.
In the following example, we will show how you can create a controller action for displaying the
feedback form and retrieving the data submitted by the user. To start, add the contact-us.phtml
view template in the application/index/ directory under the modules view/ directory (see figure
7.6 for example).
Put the HTML markup code of the feedback form from the previous section into the view
template file.
Then, add the contactUsAction() action method to the IndexController class. In the action
method, we want to extract raw data from the feedback form submitted by the site user:

Collecting User Input with Forms

192

Figure 7.6. Creating the contact-us.phtml file

1
2

<?php
namespace Application\Controller;

3
4

// ...

5
6

class IndexController extends AbstractActionController {

// This action displays the feedback form


public function contactUsAction() {

8
9
10

// Check if user has submitted the form


if($this->getRequest()->isPost()) {

11
12
13

// Retrieve form data from POST variables


$data = $this->params()->fromPost();

14
15
16

// ... Do something with the data ...


var_dump($data);

17
18

19
20

// Pass form variable to view


return new ViewModel(array(
'form' => $form
));

21
22
23
24

25
26

In the code above, we define the contactUsAction() action method in the IndexController
class (line 9).
Then, in line 12, we check whether the request is a POST request (checking the starting line of
the HTTP request). Typically, the form uses the POST method for submitting the data. For this

Collecting User Input with Forms

193

reason, we can detect if the form is submitted or not by checking the starting line of the HTTP
request.
In line 15 we retrieve the raw data submitted by the user. We extract all the POST variables with
the help of the Params controller plugin. The data is returned in the form of an array and saved
into the $data variable.
Finally, we have to add a literal route to make a short and memorable URL for the Contact Us
page. Add the following contactus key to the routing configuration in the module.config.php
file:
<?php
return array(
// ...
'router' => array(
'routes' => array(
// Add the following routing rule for the "Contact Us" page
'contactus' => array(
'type' => 'Zend\Mvc\Router\Http\Literal',
'options' => array(
'route'
=> '/contactus',
'defaults' => array(
'controller' => 'Application\Controller\Index',
'action'
=> 'contactUs',
),
),
),
),
),
),
// ...
);

Now, if you type the http://localhost/contactus URL in your web browsers navigation bar,
you should see the page as in figure 7.7. Enter an E-mail, subject, and body text and click the
Submit button on the form. The data will be sent to the server, and finally extracted in the
IndexController::contactUsAction() method.
Below, an example of the $data array (produced with the var_dump() PHP function) is shown.
As you can see, the array contains a key for each form field, including the submit field.

194

Collecting User Input with Forms

array (size=4)
'email' => string 'name@example.com' (length=16)
'subject' => string 'Happy New Year!' (length=15)
'body' => string 'Dear Support, I'd like to thank you for the
excellent quality of your support service and wish you
a happy new year!' (length=118)
'submit' => string 'Submit' (length=6)

Figure 7.7. Feedback Form

7.5 Forms and Model-View-Controller


In the previous section, weve considered a very simple form usage case: we prepared the view
template with form HTML markup and a controller action responsible for displaying the form
and dumping raw user input to the screen. However, using raw user input in real-life applications

195

Collecting User Input with Forms

has a disadvantage in that we do not check user-submitted data for possible errors and/or
malicious code. Here we will discuss how to perform such validation.
In a ZF2-based web site that uses the Model-View-Controller pattern, form functionality is
usually separated into form models responsible for field definition, filtering and validation; and
form presentation (view) which is typically implemented with the help of special view helpers.
The functionality allowing to create form models, add validation rules and use view helpers, is
schematically shown in figure 7.8. As you can see from the figure, the standard HTML forms
functionality is used as a base.

Figure 7.8. Form Functionality in ZF2

The MVC approach to working with forms has the following advantages:
You are able to reuse your form model in different controllers actions.
By using the view helpers, you can avoid the boring work of preparing HTML markup for
rendering the form and its possible validation errors.
You are able to create one or several visual representations for the same form model.
By encapsulating the form validation logic in a single form model class you have fewer
places in your code where you need to check user input, thus you improve your site
security.

7.5.1 A Typical Form Usage Workflow


Generally speaking, you instantiate a form model inside of your controllers action method,
then you retrieve the user-submitted data from PHP variables, and pass it to the form model for
validation. Form view helpers are used in a view template for generating HTML markup of the
form. This typical workflow is illustrated by figure 7.8.
Arrows in figure 7.8 denote the direction of the actions:
1. First, inside of the controllers action method, you retrieve the data submitted by the site
user from GET, POST (and possibly other) PHP variables. Then you create an instance of

Collecting User Input with Forms

196

the form model and pass it the user-submitted data. The form models work is to check
(validate) the data for correctness, and if something is wrong, produce error message(s) for
any invalid form field.
2. Secondly, you pass the form model to the .phtml view template for rendering (with the
help of the ViewModel variable container). The view template then will be able to access
the form model and call its methods.
3. And finally, the view template uses the form model and the view helpers provided by Zend
Framework 2 to render the form fields (and to display possible validation error messages
produced at the validation stage). As a result, the HTML markup of the form is produced.
In the following sections, we will discuss these in more detail.

Figure 7.8. Working with form in an MVC application

7.6 A Form Model


A form model is usually a PHP class which creates a number of fields. The base class for all form
models is the Form class defined in the Zend\Form component.
Fields in a form model can optionally be grouped into fieldsets. Moreover, the form model itself
can be considered as a fieldset. This fact is reflected in form class inheritance (figure 7.9). As you

197

Collecting User Input with Forms

can see from the figure, the Form class extends the Fieldset class. The Fieldset class, in turn,
is derived from the Element class which represents a single form field and its attributes.
This class inheritance may look strange at first sight, but everything becomes logical
if you remember that the Form class inherits methods for adding form fields from the
Fieldset class, and that it inherits methods for setting form attributes from the Element
class.

Below, we provide a stub model class for the feedback form from our previous examples:

Figure 7.9. Form class inheritance

1
2

<?php
namespace Application\Form;

3
4

use Zend\Form\Form;

5
6
7
8
9
10
11
12
13

// A feedback form model


class ContactForm extends Form
{
// Constructor.
public function __construct()
{
// Define form name
parent::__construct('contact-form');

14
15
16
17

// Set POST method for this form


$this->setAttribute('method', 'post');

Collecting User Input with Forms

198

// (Optionally) set action for this form


$this->setAttribute('action', '/contactus');

18
19
20

// Create the form fields here ...

21

22
23

As you can see, form models of the web sites Application module (by convention) belong to
Application\Form namespace (line 2).
In line 7, we define the ContactForm form model class which extends the Form base class.
In line 10, we define the constructor method for the class. Because we derive our form model
from the base Form class, we have to call the parent class constructor to initialize it (line 13).
The parent class constructor accepts an optional argument allowing it to set the forms name
(contact-form).
We can also set form data delivery method (POST) by using the setAttribute() method
provided by the base class (line 16). The setAttribute() takes two parameters: the first one
is the name of the attribute to set, and the second one is the value of the attribute.
You also can set the forms action attribute (line 19) with the setAttribute() method,
analogous to the way you did with the name attribute. Actually, as you will see later, setting
the forms action attribute is optional.
Setting the action attribute for the form is optional, because empty form action forces
the browser to submit form data to the URL of the current page. This is sufficient in
most scenarios, because usually you use the single controller action for both displaying
the form and processing its data.

Form fields are typically created inside of the form models constructor (look at line 21). In the
next section, we will learn which form fields are available and how to add them to the form
model.

7.7 Form Elements


In a form model, an input field is typically paired with the text label (<label> and <input> tags
are used together). Such a pair is also called a form models element.
Analogous to an HTML form field, a form models element may contain the name and other
(optional) attributes (e.g. id, class, etc.) Additionally, you may set options to an element; the
options mostly allow you to specify the text and attributes for the elements label.
All form models elements are inherited from the base class Element which also belongs to the
Zend\Form component. The Element base class implements the ElementInterface interface. The
class inheritance diagram is shown in figure 7.10.

199

Collecting User Input with Forms

Figure 7.10. Form element class inheritance

Concrete form element classes extend the Element base class. They are listed in table 7.3. These
classes live in the Zend\Form\Element namespace.
Table 7.3. Form elements

Class name

Description

Elements compatible with HTML4


Button
Checkbox
File
Hidden
Image
Password
Radio
Select
Submit
Text
Textarea

Button.
Check box.
File field.
Hidden field.
Image field.
Password field.
Radio button.
Dropdown list.
Submit button.
General-purpose text input field.
Multi-line text area.

HTML5 Elements
Color
Date
DateTime
DateTimeLocal
Email
Month
Number
Time
Url
Week
Range

Color picker.
Date picker.
Date & time (with time zone).
Date & time (without time zone).
E-mail field.
Month input field.
A text input field accepting numbers.
Text input field for entering time.
Text input field for entering an URL.
Text input field for entering days of week.
Range field (slider).

Compound Fields
MultiCheckbox
DateTimeSelect
DateSelect
MonthSelect

Security Form Elements

A group of related check boxes.


Date & time select.
Date select.
Month select.

200

Collecting User Input with Forms

Table 7.3. Form elements

Class name

Description

Captcha
Csrf

Human check image.


Cross-site request forgery prevention.

Other
Collection

Element collection.

In the table above, you can see that the ZF2-provided form elements have direct mapping on
HTML4 and HTML5 input fields (discussed in the beginning of this chapter).
For your convenience, ZF2 also provides several compound fields. The MultiCheckbox field
is a field which is composed of a group of typical checkboxes related to each other. The
DateTimeSelect, DateSelect, and MonthSelect elements are analogous to corresponding HTML5
elements, but simulate them with the usual select fields. These input fields have an advantage in
that they are supported by all web browsers, unlike the corresponding HTML5 fields. The visual
representation of these elements can be seen in figure 7.11.

Figure 7.11. Compound form fields

Additionally, ZF2 provides security form fields Captcha and Csrf, which can be used on a form
for enhancing the security. The Captcha element is a graphical element (image) that is placed
on a form for checking if the site user is a human or a robot. The Csrf element has no visual
representation and is used for prevention of hacker attacks related to cross-site request forgery
.
There is another special form element called Collection. This element is analogous to fieldset,
because it allows you to group related form elements. But, it is designed for adding form elements
dynamically by binding an array of objects to the form. We will discuss the usage of collections
later in Chapter 9.
Cross-site request forgery (CSRF) is a type of malicious exploit of a website whereby unauthorized commands are transmitted from a user
that the website trusts.

201

Collecting User Input with Forms

7.7.1 Adding Elements to a Form Model


The methods inherited by the Form base class from the Fieldset class are used to add elements
(and fieldsets) to the form model. These methods are summarized in the table 7.4.
Table 7.4. Methods provided by the Fieldset class

Method name

Description

add($elementOrFieldset, $flags)
has($elementOrFieldset)
get($elementOrFieldset)
getElements()
getFieldsets()
count()
remove($elementOrFieldset)

Attaches an element (or fieldset).


Checks whether certain element is attached.
Retrieves the given element (or fieldset) by name.
Retrieves all attached elements.
Retrieves all attached fieldsets.
Return the count of attached elements/fieldsets.
Removes the element (or fieldset).

Particularly, we are interested in the add() method which is used to attach an element to a form.
The add() method takes two arguments: the first one (named $elementOrFieldset) is an element
to insert, and the second one (named $flags) is the optional flags.
The $elementOrFieldset parameter may either be an instance of an Element-derived class (or
the Fieldset class), or an array describing the element that should be created.
The optional $flags argument is an array which may contain a combination of the following
keys: name (allows you to set the elements name) and priority (allows to specify the zerobased index in the list of elements to insert the element to. If the priority flag is not specified,
the element will be inserted at the end of the list of the form models elements.
Below, we provide two code examples illustrating the possible ways of adding elements to a
form.

7.7.2 Method 1: Passing an Instance of an Element


The following code fragment creates an instance of the Zend\Form\Element\Text class and adds
the element to the form model:
1
2

<?php
namespace Application\Form;

3
4
5
6

// Define an alias for the class name


use Zend\Form\Form;
use Zend\Form\Element\Text;

7
8
9
10
11
12

// A feedback form model


class ContactForm extends Form
{
// Constructor.
public function __construct()

202

Collecting User Input with Forms

13

// Create the form fields here ...


$element = new Text(
'subject',
// Name of the element
array(
// Array of options
'label'=> 'Subject' // Text label
));
$element->setAttribute('id', 'subject');

14
15
16
17
18
19
20
21

// Add the "subject" field to the form


$this->add($element);

22
23

24
25

In the code above, we created an instance of the Zend\Form\Element\Text class (line 15). The
class constructor takes two parameters: the elements name (subject) and an array of options
(here we specify the text label Subject).
Additionally, you may configure the element using the methods provided by the Element base
class. For example, in line 20, we set the id attribute with the setAttribute() method. For
your reference, the (most important) methods of the Element base class which can be used for
configuring a form element are presented in table 7.5.
Table 7.5. Methods provided by the Element class

Method name

Description

setName($name)
getName()
setOptions($options)
getOptions($options)
getOption($option)
setAttribute($key, $value)
getAttribute($key)
removeAttribute($key)
hasAttribute($key)
setAttributes($arrayOrTraversable)
getAttributes()
clearAttributes()
setValue()
getValue()
setLabel()
getLabel()
setLabelAttributes()
getLabelAttributes()
setLabelOptions()
getLabelOptions()

Sets elements name.


Retrieves elements name.
Sets options.
Retrieves options.
Retrieves the given option.
Sets a single element attribute.
Retrieves a single element attribute.
Removes an attribute.
Checks whether such an attribute presents.
Removes an attribute.
Retrieves all attributes at once.
Removes all attributes at once.
Sets the element value.
Retrieves the element value.
Sets the label used for this element.
Retrieves the label string used for this element.
Sets the attributes to use with the label.
Gets the attributes to use with the label.
Sets label specific options.
Retrieves label specific options.

203

Collecting User Input with Forms

7.7.3 Method 2: Using Array Specification


The second example below (equivalent to the first one) shows how to use an array specification
to add an element to form. This method is preferable, because it requires less code to write.
When using array specification for adding an element to a form, the element will
automatically be instantiated and configured by the base class. Internally, this is
accomplished with the help of the Zend\Form\Factory factory class (illustrated by
figure 7.12).

1
2

<?php
namespace Application\Form;

3
4
5

// Define an alias for the class name


use Zend\Form\Form;

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

// A feedback form model


class ContactForm extends Form
{
// Constructor.
public function __construct()
{
// Add "subject" field
$this->add(array(
'type' => 'text',
'name' => 'subject',
'attributes' => array(
'id' => 'subject',
),
'options' => array(
'label' => 'Subject',
),
));
}
}

// Element type
// Field name
// Array of attributes

// Array of options
// Text label

204

Collecting User Input with Forms

Figure 7.12. The logic of the add() method

In line 14 above, we call the form models add() method to add the element to form. We pass the
element specification to the add() method in the form of an array. The array has the following
typical keys:
the type key (line 15) defines the class name to use for instantiation of the element. Here
you can use either the full class name (e.g. Zend\Form\Element\Text) or its short alias
(e.g. text).
the name key (line 16) defines the name for the field (subject).
the attributes key (line 17) defines the list of HTML attributes to set (here we set the id
attribute).
the options array (line 18) allows you to specify the text label for the element.

7.8 Example: Creating the Contact Form Model


Now that we know how to set the form name, action, and method attributes and how to add
fields (elements) to the form, lets create the complete model class for the feedback form that we
used in our previous examples.
As we know, form model classes for the Application module live inside the Application\Form
namespace. So, we have to create the ContactForm.php file inside of the Form directory under
the Application modules source directory (figure 7.13).
If

you are confused where we take element aliases from, than you should know that they are defined inside of the

Zend\Form\FormElementManager class.

205

Collecting User Input with Forms

Figure 7.13. Form directory

We will have two methods in our form class:


__construct() constructor will define the form name and method (POST), and initialize
the form by adding its elements;
addElements() protected method will contain the actual code for adding form elements
and will be called by the constructor.

We put the field creation logic into the addElements() protected method to better
structure the form models code.

The code of the ContactForm class is presented below:


1
2

<?php
namespace Application\Form;

3
4

use Zend\Form\Form;

5
6
7
8
9
10
11
12
13
14
15

/**
* This form is used to collect user feedback data like user E-mail,
* message subject and text.
*/
class ContactForm extends Form
{
// Constructor.
public function __construct()
{
// Define form name

Collecting User Input with Forms

parent::__construct('contact-form');

16
17

// Set POST method for this form


$this->setAttribute('method', 'post');

18
19
20

// Add form elements


$this->addElements();

21
22
23

24
25
26
27

// This method adds elements to form (input fields and


// submit button).
private function addElements() {

28
29
30
31
32
33
34
35
36
37
38
39

// Add "email" field


$this->add(array(
'type' => 'text',
'name' => 'email',
'attributes' => array(
'id' => 'body'
),
'options' => array(
'label' => 'Your E-mail',
),
));

40
41
42
43
44
45
46
47
48
49
50
51

// Add "subject" field


$this->add(array(
'type' => 'text',
'name' => 'subject',
'attributes' => array(
'id' => 'subject'
),
'options' => array(
'label' => 'Subject',
),
));

52
53
54
55
56
57
58
59
60
61

// Add "body" field


$this->add(array(
'type' => 'text',
'name' => 'body',
'attributes' => array(
'id' => 'body'
),
'options' => array(
'label' => 'Message Body',

206

Collecting User Input with Forms

207

),

62

));

63
64

// Add the submit button


$this->add(array(
'type' => 'submit',
'name' => 'submit',
'attributes' => array(
'value' => 'Submit',
),
));
}

65
66
67
68
69
70
71
72
73
74

In line 10 above, we define the ContactForm class which extends the Form base class.
In lines 13-23, we have the constructor method. It calls the base class constructor (line 16) and
passes the form name as its argument (contact-form). In line 19, the base class setAttribute()
method is called allowing you to set the method name for the form (we set the POST method).
In line 22, the addElements() protected method is called, which does the actual work of adding
elements to the form. The code of the addElements() is located in lines 27-70. To add elements
to the form, we call the add() method provided by the base class. This method accepts the single
argument an array containing configuration for an element. We add four fields: the email, the
subject, the body and the submit field.
In figure 7.14, you can see schematic graphical representation of the form model we have created.

Figure 7.14. The feedback form model and its elements

Collecting User Input with Forms

208

7.9 Adding Form Validation Rules


Form validation is the procedure of filtering and checking the data passed to the server during
the form submission. For example, for our feedback form, we want to perform the following
checks:
We want to test that the E-mail address, message subject, and body fields are always
present (because these fields are required).
We want to ensure that the user entered a valid E-mail address like name@example.com.
Users may add white space characters to the beginning and/or the end of the E-mail
address, so we would like to filter such characters out (perform the string trimming
operation).
It would be useful to check for minimum and maximum allowed length of the message
subject and body text.
For the message subject, we would like to filter out (strip) the new line characters and
HTML tags .
We also want to strip HTML tags from the message body.
The requirements above are called validation rules. Validation rules can be divided into two
categories: filters and validators.
The filters transform the user-entered data to fix possible errors or to ensure the data conforms
to a certain format. Filters are typically applied first, validators are applied in the last turn.
Validators check whether the data is acceptable or not. If all data is correct, the form is considered
valid and the data can be safely used by the business logic layer. If a certain field is invalid, a
validator raises an error flag. In that case, the form is typically shown to the user again, and the
user is asked to correct any input errors and resend the form to server.
What happens if I dont add a validation rule for a certain form field?
If you do not add a validation rule then the user-submitted field value will not be
checked, leaving a hole in your sites security. It is recommended to always add a
validation rule per each form field entered by user and add as many checks per each
field as needed to keep your form secure.

7.9.1 Input Filter


In ZF2, you store the validation rules with the help of the InputFilter class. The InputFilter
class is defined in the Zend\InputFilter component. The input filter is a container for so called
inputs. Typically, you add an input per each form models field you have.
There may be malicious users inserting HTML code in the message. If you open such code in your browser, you may see some undesired
content. To avoid this, we need to replace HTML tags in message subject and text.

Collecting User Input with Forms

209

An input may consist of filters and/or validators and some additional information. For
example, an input may contain the flag telling if the field is required or if its value may
be missing from HTTP request.

Analogous to adding a form models fields, there are two possible ways of adding inputs to the
input filter container: either via passing an instance of an input class as the argument of its add()
method, or via passing the array specification . In the next section, we will describe the latter
method (it is preferable, because it requires less code to write).

7.9.2 Adding Inputs to Input Filter


To add an input to the input filter, you use its add() method, which takes the single argument an array specification of the input in the following form:
1
2
3
4
5
6
7
8
9
10
11

array(
'name'
=> '<name>',
'type'
=> '<type>',
'required' => <required>,
'filters' => array(
// Add filters configuration here ...
),
'validators' => array(
// Add validators configuration here ...
)
)

In above array, we have the following keys:


The name key (line 2) defines the name of the input. The name should be the same as the
name of the form models field. If the name of the input doesnt match the name of the
corresponding form models field, the validation rule wont be applied to the field.
The type key (line 3) defines the class name of the input. This key is optional. By default
(when this key is omitted), the Zend\InputFilter\Input class is used. Available input
classes are shown in figure 7.15. In figure 7.15, the Input class is designed to be used
with regular scalar values, ArrayInput is used for filtering/validating array values, and
FileInput is used for checking uploaded files.
The required key (line 4) tells whether the form field is required or optional. If the field
is required, the site user will have to fill it in; otherwise he will receive a validation error.
the filters (line 5) and validators (line 8) keys may contain the configuration for zero,
one, or several filters and/or validators applied to the form models field.
In the latter (array specification) case, the input will be automatically created with the help of the Zend\InputFilter\Factory class.

210

Collecting User Input with Forms

Figure 7.15. Input class inheritance

7.9.2.1 Filter Configuration


A typical filter configuration is presented below:
1
2
3
4
5
6
7

array(
'name' => '<filter_name>',
'priority' => <priority>
'options' => array(
// Filter options go here ...
)
),

The name key (line 2) is the name for the filter. This may be either a full filter class name (e.g.
Zend\Filter\StringTrim or an alias (e.g. StringTrim).
The optional priority key (line 3) defines filter priority in the list of filters. The priority must
be an integer number. The filters with the highest priority will be applied first. By default, the
FilterChain::DEFAULT_PRIORITY constant (value 1000) is assigned.
The options array (line 4) is specific to a certain filter and may contain parameters for
configuring the filter.
7.9.2.2 Validator Configuration
A typical validator configuration is presented below:

211

Collecting User Input with Forms


1
2
3
4
5
6
7

array(
'name' => '<validator_name>',
'break_chain_on_failure' => <flag>
'options' => array(
// Validator options go here ...
)
),

The name key (line 2) is the name for the validator. This may be either a full validator class name
(e.g. Zend\Validator\EmailAddress or an alias (e.g. EmailAddress).
The break_chain_on_failure optional key (line 3) defines the behavior in case the validator
check fails. If this equals to true, subsequent validators in the list will not be executed; otherwise
every validator in the list will be executed without depending on the result of other validators.
The options array (line 4) is specific to certain validator class and may contain parameters for
configuring the validator.
7.9.2.3 Attaching Input Filter to Form Model
Once you have created and populated the input filter container, you have to attach it to the form
model. The Form base class provides the setInputFilter() method intended for this purpose
(see table 7.6).
Table 7.6. Methods provided by the Form base class

Method name

Description

setInputFilter($inputFilter)
getInputFilter()

Attaches the input filter container to the form.


Retrieves the input filter attached to the form.

7.9.3 Creating Input Filter for the Contact Form


Now that you have a general idea on know how to define the input filter container and populate it
with filters and validators for each form field, lets complete our ContactForm form model class.
Below, we add the addInputFilter() private method, which defines the filtering/validation
rules, stores them in input filter container, and attaches the input filter to the form model:
1
2
3

<?php
// ...
use Zend\InputFilter\InputFilter;

4
5
6
7
8
9

class ContactForm extends Form


{
public function __construct()
{
// ... call this method to add validation rules

Collecting User Input with Forms

$this->addInputFilter();

10
11

12
13

// ...

14
15
16

// This method creates input filter (used for form filtering/validation).


private function addInputFilter() {

17
18
19

$inputFilter = new InputFilter();


$this->setInputFilter($inputFilter);

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

$inputFilter->add(array(
'name'
=> 'email',
'required' => true,
'filters' => array(
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'EmailAddress',
'options' => array(
'allow' => \Zend\Validator\Hostname::ALLOW_DNS,
'useMxCheck' => false,
),
),
),
)
);

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

$inputFilter->add(array(
'name'
=> 'subject',
'required' => true,
'filters' => array(
array('name' => 'StringTrim'),
array('name' => 'StripTags'),
array('name' => 'StripNewLines'),
),
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'min' => 1,
'max' => 128
),
),
),

212

Collecting User Input with Forms

213

)
);

56
57
58

$inputFilter->add(array(
'name'
=> 'subject',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
),
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'min' => 1,
'max' => 4096
),
),
),
)
);

59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

76
77

As you can see from the code above, first we declare the alias for the Zend\InputFilter\InputFilter
class (line 3).
In the form models constructor (line 10), we call the addInputFilter() method which we define
in lines 16-76.
The addInputFilter() methods goal is to create the InputFilter container (line 18), attach it
to form model (line 19) and add filtering/ validation rules (lines 21-75). For attaching the input
filter to the form model, we use the setInputFilter() method provided by the Form base class.
For inserting filtering/validation rules into the input filter container, we use the add() method
provided by the InputFilter class, which takes the array specification of an input to create.
We add three inputs (per each field of our form model, except its submit button):
For the email field, we set the required flag to true to make filling this field mandatory.
We use StringTrim filter to remove white spaces from the beginning and the end of the Email address; and the EmailAddress validator for checking the user-entered E-mail address
for correctness. We configure the EmailAddress validator to allow domain names as Email addresses (the \Zend\Validator\Hostname::ALLOW_DNS flag) and disable MX record
checking (set useMxCheck option to false).
For the subject field, by analogy, we make it required, and use the StringTrim filter
to remove white spaces from the beginning and the end. Additionally, we use the
StripNewLines and StripTags filters to filter out the new line characters and HTML tags,
respectively. We constrain subject string length to be between 1 and 128 characters in
length by using the StringLength validator.

Collecting User Input with Forms

214

For the body field, we require it to be mandatory, and we use the StripTags filter to strip
HTML tags from E-mail text. We also use the StringLength validator to constrain E-mail
text to be between 1 and 4096 characters in length.
In figure 7.16, you can find the schematic graphical representation of the input filter weve
created.

Figure 7.16. The input filter for ContactForm

Above, we briefly described how to create an input filter for the form model. For
detailed information about the above mentioned (and other) filters and validators and
their usage examples, please refer to Chapter 8.

7.10 Using the Form in a Controllers Action


When the form model class is ready, you finally can use the form in a controllers action method.
As you might already know, the way the site user works with form is typically an iterative
process (schematically illustrated by figure 7.17):

215

Collecting User Input with Forms

Figure 7.17. Typical form usage workflow

First, you display the form and its fields on a web page, prompting user for input. Once
the user fills the form fields, he clicks the Submit button and sends the data to server.
Next, your controller extracts the submitted data and asks the form model to validate it. If
there were input errors, you display the form again, asking the user to correct input errors.
If the data is correct, you process the data with your business logic layer and (usually)
redirect the user to another web page.
The Form base class provides several methods for accomplishing these (see table 7.7).

216

Collecting User Input with Forms

Table 7.7. Methods provided by the Form base class

Method name

Description

setData($data)
getData($flag)
isValid()
hasValidated()
getMessages($elementName = null)

Sets form data for validation.


Retrieves the validated data.
Validates the form.
Check if the form has been validated.
Returns a list of validation failure messages, if any,
for a single element or for all form elements.

So, a generic form usage workflow is the following:


Check whether the form data has been submitted, and if not, display the form on the web
page.
If the data has been submitted by site user, the raw data is retrieved from POST (and/or GET
) variables in the form of an array.
The data is assigned to the form models fields using the forms setData() method.
The filtering and validation is performed using the forms isValid() method (this results
in executing the input filter attached to the form). If a certain field(s) is/are invalid, display
the form again and ask the user to correct their input.
As soon as the data has been filtered/validated you retrieve the data from the form model
using the getData() method and can pass the data to other models or use it any other way.
The code example below illustrates how to implement this typical workflow in your controllers
action method:
1
2

<?php
namespace Application\Controller;

3
4
5

use Application\Form\ContactForm;
// ...

6
7

class IndexController extends AbstractActionController {

8
9
10

// This action displays the feedback form


public function contactUsAction() {

11
12
13

// Create Contact Us form


$form = new ContactForm();

14
15
16

// Check if user has submitted the form


if($this->getRequest()->isPost()) {

17
18
19

// Fill in the form with POST data


$data = $this->params()->fromPost();

Collecting User Input with Forms

217

$form->setData($data);

20
21

// Validate form
if($form->isValid()) {

22
23
24

// Get filtered and validated data


$data = $form->getData();

25
26
27

// ... Do something with the validated data ...

28
29

// Redirect to "Thank You" page


return $this->redirect()->toRoute('application/default',
array('controller'=>'index', 'action'=>'thankYou'));

30
31
32

33

34
35

// Pass form variable to view


return new ViewModel(array(
'form' => $form
));

36
37
38
39

40
41

In the code above, we define the contactUsAction() action method in the IndexController
class (line 10). In the action method, we create an instance of the ContactForm class (line 13).
Then, in line 16, we check whether the request is a POST request (checking the starting line of
HTTP request).
In line 19 we retrieve the raw data submitted by the user. We extract all the POST variables with
the help of the Params controller plugin. The data is returned in the form of an array and saved
into the $data variable.
The data submitted by the user may contain mistakes and should be filtered and validated before
further usage. To do that, in line 20 we set the data to the form model with the setData()
method provided by the Form base class. We validate form data with the isValid() method (line
23), which returns true upon successful validation. If the validation succeeds, we retrieve the
validated data using the getData() method (line 26) and then can pass the data to our business
logic layer.
Once we have used the validated data, in line 31, we redirect the web user to the Thank You page.
The redirect is performed with the Redirect controller plugin. The Redirect plugins toRoute()
method takes two parameters: the first parameter is the name of the route (application/default),
and the second one is the array of parameters to pass to the router. These identify the web page
where you redirect the user.
We will prepare the controllers action and view template for the Thank You page a
little bit later.

Collecting User Input with Forms

218

In line 37, we pass the form model through the $form variable to the view template. The view
template will access this variable and will use it for rendering the form (and possible validation
errors).

7.10.1 Passing Form Data to a Model


To give you a real-life example of how you can use the validated data of the feedback form, in
this section we will create a simple MailSender model class which can be used for sending an
E-mail message to an E-mail address. When the user submits the form, we will validate the form
data and pass the validated data to the MailSender model and ask it to send the E-mail message
to the recipient.
Reading this section is optional and intended mostly for beginners. You may skip it and
refer directly to the next section Form Presentation.

The MailSender model will internally use the Zend\Mail component. The Zend\Mail component is a component provided by Zend Framework 2 and designed to give you the convenient functionality for composing mail messages (the Zend\Mail\Message class) and several
classes implementing available transports for sending mail (in this example, we will use the
Mail\Transport\Sendmail class which uses the sendmail program for delivering E-mails).
The sendmail program is a free open-source mail transfer agent for Linux/Unix
operating systems. It accepts messages that a PHP script passes to it, deciding based
upon the message header which delivery method it should use, and then passes the
message through the SMTP protocol to the appropriate mail server (like Google Mail)
for delivery to the recipient.

Start with creating the MailSender.php file under the Service directory under the modules source
directory (see figure 7.18 for example).

Figure 7.18. Creating the MailSender.php File

The following is the code that should be put into the MailSender.php file:

In DDD terms, the MailSender can be related to service models, because its goal is to manipulate data, not to store data.
http://www.sendmail.com/sm/open_source/

Collecting User Input with Forms


1
2

219

<?php
namespace Application\Service;

3
4
5
6

use Zend\Mail;
use Zend\Mail\Message;
use Zend\Mail\Transport\Sendmail;

7
8
9

// This class is used to deliver an E-mail message to recipient.


class MailSender {

10

// Sends the mail message.


public function sendMail($sender, $recipient, $subject, $text) {

11
12
13

$result = false;
try {

14
15
16

// Create E-mail message


$mail = new Message();
$mail->setFrom($sender);
$mail->addTo($recipient);
$mail->setSubject($subject);
$mail->setBody($text);

17
18
19
20
21
22
23

// Send E-mail message


$transport = new Sendmail('-f'.$sender);
$transport->send($mail);
$result = true;
} catch(\Exception $e) {
$result = false;
}

24
25
26
27
28
29
30
31

// Return status
return $result;

32
33

34
35

In the code above, we define the Application\Service namespace (line 2), because the
MailSender class can be related to service models (its goal is to manipulate data, not to store
it).
In lines 4-6, we declare the aliases for the Mail, Message and Transport\Sendmail classes
provided by the Zend\Mail component.
In lines 9-35, we define the MailSender class. The class has the single method sendMail() (line
12), which takes four arguments: senders E-mail address, recipients E-mail address, message
subject and, finally, message body text.

Collecting User Input with Forms

220

In line 18, we create an instance of the Message class. We use the methods provided by this class
for composing the message (set its subject, body etc.) in lines 19-22.
In line 25, we create an instance of the Sendmail class, which uses the sendmail program to pass
the message to the appropriate mail server (see lines 25-26). Since the classes provided by the
Zend\Mail component may throw an exception on failure, we enclose the block of code with the
try-catch exception handler.
The sendMail() method will return true if the E-mail message sent successfully; otherwise it
will return false (line 33).
Configuring mail system for your web server is a rather complex task. It typically
requires installing sendmail and configuring the servers MX DNS record to use certain
mail server (either local mail server, e.g. Posftix, or remote server, like Google Mail).
Because of the complexity of the topic, it is not discussed in this book. You can find
additional information on configuring mail for your particular system online.

Finally, you can instantiate the MailSender model in your IndexController::contactUsAction()


method and pass it the validated form data. Below, the complete code for the contactUsAction()
method is presented:
1
2
3

<?php
// ...
use Application\Service\MailSender;

4
5

class IndexController extends AbstractActionController {

6
7
8

// ...
public function contactUsAction() {

9
10
11

// Create Contact Us form


$form = new ContactForm();

12
13
14

// Check if user has submitted the form


if($this->getRequest()->isPost()) {

15
16
17

// Fill in the form with POST data


$data = $this->params()->fromPost();

18
19

$form->setData($data);

20
21
22

// Validate form
if($form->isValid()) {

23
24

// Get filtered and validated data


http://www.postfix.org/

Collecting User Input with Forms

221

$data = $form->getData();
$email = $data['email'];
$subject = $data['subject'];
$body = $data['body'];

25
26
27
28
29

// Send E-mail
$mailSender = new MailSender();
if(!$mailSender->sendMail(
'no-reply@example.com', $email, $subject, $body)) {
// In case of error, redirect to "Error Sending Email" page
return $this->redirect()->toRoute('application/default',
array('controller'=>'index', 'action'=>'sendError'));
}

30
31
32
33
34
35
36
37
38

// Redirect to "Thank You" page


return $this->redirect()->toRoute('application/default',
array('controller'=>'index', 'action'=>'thankYou'));
}

39
40
41
42

43
44

// Pass form variable to view


return new ViewModel(array(
'form' => $form
));

45
46
47
48

49
50

// This action displays the Thank You page. The user is redirected to this
// page on successful mail delivery.
public function thankYouAction() {
return new ViewModel();
}

51
52
53
54
55
56

// This action displays the Send Error page. The user is redirected to this
// page on mail delivery error.
public function sendErrorAction() {
return new ViewModel();
}

57
58
59
60
61
62

As you can see from the code above, we do the following:


In line 3, we declare an alias for Application\Service\MailSender class. This will allow
you to refer to the model class by its short name.
In lines 26-28, after weve validated the form, we extract the validated field values into the
$email, $subject and $body PHP variables.

222

Collecting User Input with Forms

In line 31, we instantiate the MailSender class with the new operator; in line 32, we call its
sendMail() method and pass it four parameters: the senders address (here we use noreply@example.com, but you can replace this with the address of your sendmail); the
recipients E-mail address, the E-mail subject and body.
If mail has been sent successfully (if the sendMail() method returned true), we redirect
the user to the Thank You page (line 40). On failure (if sendMail() method returned false),
we redirect the user to the Send Error page (line 35).
In lines 53-55, we have the thankYouAction() method which displays the Thank You page.
This page is shown if the E-mail message is sent successfully.
In line 59-61, we have the sendErrorAction() method which shows the Error Sending
Email page. This page is shown on E-mail delivery failure.

7.11 Form Presentation


When your controllers action is ready, all you have to do is prepare the .phtml view template
file to display your form on a web page. In the view template, you need to define the markup
using <form>, <label>, <input>, and possibly other HTML tags.
Additionally, you will have to display error messages if the form validation failed. Because this
work is rather boring, Zend Framework 2 provides you with special view helpers intended for
rendering the form.
For simple forms (which do not show error messages), you can use raw HTML tags for
rendering the form and ignore ZF2-provided form view helpers. But, form view helpers
are really unavoidable when rendering complex forms that may display validation
errors and/or add fields dynamically.

7.11.1 Preparing the Form Model for Rendering


Before rendering, it is required that you call the prepare() method on the form models instance
(see table 7.8). If you forget to call this method, there may be undesired effects.
Table 7.8. Methods provided by the Form base class

Method name

Description

prepare()

Ensures the form state is ready for use.

The prepare() method does the following form model preparations:


It calls the input filter container attached to the form model, to ensure validation error
messages are available;
It prepares any elements and/or fieldsets that require preparation .
Typically, this results in wrapping field names with the form/fieldset name (for example, the email fields name will become contactform[email]) which technically results in a more convenient field grouping in a HTTP request body.

Collecting User Input with Forms

223

7.12 Standard Form View Helpers


Standard form view helpers provided by ZF2 are shown in table 7.9. These classes live in the
Zend\Form\View\Helper namespace. As you can see from the table, the view helpers can be
divided into the following categories:
Generic form view helpers. These classes are designed to render the whole form (Form
helper) or its single element (FormElement helper) and possible validation errors (FormElementErrors
helper).
View helpers for rendering HTML fields of certain types. These allow you to generate
HTML markup for concrete form fields (e.g. FormButton, FormRadio, etc.) and a text label
(FormLabel).
View helpers for rendering form fields introduced in HTML5. These are analogous to the
view helpers from the previous category, but intended for rendering HTML5 fields (e.g.
FormDate, FormUrl, etc.)
Other view helpers. In this category, we can put the view helper classes designed for
rendering ZF2-specific fields, like FormMultiCheckbox, FormCaptcha, etc.

Table 7.9. View helpers designed for using with forms

Method name

Description

Generic helpers
Form
FormElement
FormElementErrors
FormRow

Renders the entire form and all its elements.


Renders a generic form element.
Renders validation errors for a form element.
Renders the label, the field and validation errors.

HTML field helpers


FormButton
FormCheckbox
FormFile
FormHidden
FormInput
FormImage
FormLabel
FormPassword
FormRadio
FormReset
FormSelect
FormSubmit
FormText
FormTextarea

Renders the <button> form field.


Renders the <input type="checkbox"> field.
Renders the <input type="file"> form field.
Renders the <input type="hidden"> form field.
Renders an <input> form field.
Renders the <input type="image"> form field.
Renders the <label> tag.
Renders the <input type="password"> form field.
Renders the <input type="radio"> form field.
Renders the <input type="reset"> form field.
Renders the <select> dropdown field.
Renders the <input type="submit"> form field.
Renders the <input type="text"> form field.
Renders the <textarea> multi-line text field.

HTML5 field helpers


FormColor
FormDate

Renders the <input type="color"> HTML5 form field.


Renders the <input type="date"> HTML5 form field.

224

Collecting User Input with Forms

Table 7.9. View helpers designed for using with forms

Method name

Description

FormDateTime
FormDateTimeLocal
FormEmail
FormMonth
FormNumber
FormRange
FormTel
FormTime
FormUrl
FormWeek

Renders the <input


Renders the <input
Renders the <input
Renders the <input
Renders the <input
Renders the <input
Renders the <input
Renders the <input
Renders the <input
Renders the <input

type="date"> HTML5 form field.


type="datetime-local"> HTML5 form field.
type="email"> HTML5 form field.
type="month"> HTML5 form field.
type="number"> HTML5 form field.
type="range"> HTML5 form field.
type="tel"> HTML5 form field.
type="time"> HTML5 form field.
type="url"> HTML5 form field.
type="week"> HTML5 form field.

Other helpers
FormCaptcha
FormDateSelect
FormDateTimeSelect
FormMonthSelect
FormMultiCheckbox
FormCollection

Renders the CAPTCHA security field.


Renders the date select field.
Renders the datetime select field.
Renders the month select field.
Renders the multi checkbox field.
Renders the collection of elements.

In the next sections, we will provide an overview of several frequently used form view helpers
and their usage examples.

7.12.1 Rendering a Form Element


You can render a form field with the FormElement view helper. It is designed to be as flexible as
possible and recognize as many field types as possible. So, with this view helper you are able to
produce HTML markup for text fields, buttons, dropdown lists and so on.
The methods provided by this view helper are listed in table 7.10.
Table 7.10. Methods provided by the FormElement view helper

Method name

Description

render($element)

PHP magic method which renders the given form field.

__invoke($element)

PHP magic method which renders the given form field


(the effect is the same as render()).

As you can see, there are two methods doing the same thing:
The render() method produces the HTML markup for the form field. It accepts the single
argument the instance of the element to render. You can retrieve the form element with
the form models get() method (see example below).
The __invoke() method is a convenience wrapper which results in less code to write.

Collecting User Input with Forms

225

<?php
// We assume that the form model is stored in $form variable.
// Render the E-mail field with the render() method.
echo $this->formElement()->render($form->get('email')); ?>
// The same, but with __invoke
echo $this->formElement($form->get('email'));

When executed, the code above will generate the HTML code as follows:
<input type="text" name="email" id="email" value="">

Typically, there is no need to call view helpers for concrete HTML (or HTML5) fields
(e.g. FormText, FormSubmit, etc.) Instead, you can use the generic FormElement view
helper which determines the field type automatically and produces the needed HTML
code.

7.12.2 Rendering an Elements Validation Errors


The FormElementErrors view helper class allows you to produce HTML markup for field
validation errors (if present). If there are no validation errors for certain element, this view helper
does not produce any output.
An example of using the FormElementErrors view helper is presented below:
<?php
// We assume that the form model is stored in $form variable.
// Render validation errors for the E-mail field.
echo $this->formElementErrors($form->get('email'));

If there were any validation errors, this code will generate the unordered list of errors using the
<ul> HTML tag, and the list will contain as many items as there are errors for certain field. An
example of such list for the E-mail field of our feedback form is presented below:
<ul>
<li>&#039;hostname&#039; is not a valid hostname for the email address</li>
<li>The input does not match the expected structure for a DNS hostname</li>
<li>The input appears to be a local network name but local network names a\
re not allowed</li>
</ul>

7.12.3 Rendering an Elements Label


The FormLabel helper allows you to render the text label for an element:

226

Collecting User Input with Forms

<?php
// We assume that the form model is stored in $form variable.
// Render text label for the E-mail field.
echo $this->formLabel($form->get('email'));

When executed, the code above will generate the HTML code as follows:
<label for="email">Your E-mail</label>

7.12.4 Rendering a Form Row


The FormRow view helper is designed to simplify the rendering of a form field, its label, and
validation errors. With this class, you are able to render these in a single step. This helper is
flexibly configurable, so you can apply a different decoration to the form row. The methods of
this view helper class are listed in table 7.11.
Table 7.11. Methods provided by the FormRow view helper

Method name

Description

render($element)

Renders the form row.

__invoke($element, $labelPosition,
$renderErrors, $partial)

Renders the form row (convenience wrapper).

setInputErrorClass($inputErrorClass)

Sets input error CSS class.

setLabelAttributes($labelAttributes)

Sets label attributes.

setLabelPosition($labelPosition)

Sets label position (before or after the field).

setRenderErrors($renderErrors)

Set if the errors are rendered by this helper.

setPartial($partial)

Set a partial view script to use for rendering the


row.

An example of using the FormRow view helper is presented below:


<?php
// We assume that the form model is stored in $form variable.
// Render the E-mail field, its label and (possible) validation errors.
echo $this->formRow($form->get('email'));

When executed, the code above will generate the HTML code as follows:

Collecting User Input with Forms

227

<label for="email">Your E-mail</label>


<input type="text" name="email" id="email">
<ul>
<li>&#039;hostname&#039; is not a valid hostname for the email address</li>
<li>The input does not match the expected structure for a DNS hostname</li>
<li>The input appears to be a local network name but local network names ar\
e not allowed</li>
</ul>

7.12.5 Rendering the Entire Form


The Form view helper allows you to render the opening <form> tag and its attributes; and the
closing </form> tag. But its major purpose is to render the entire form and all of its fields with
a single line of code. Public methods of the Form view helper class are summarized in table 7.12.
Table 7.12. Methods provided by the Form view helper

Method name

Description

render($form)

Renders the entire form and all its elements.

__invoke($form)

PHP magic method which renders the entire form and all its
elements (the effect is the same as render()).

openTag($form)

Renders the opening <form> tag.

closeTag()

Renders the closing </form> tag.

You can render the whole form with the help of the Forms render() method as follows:
// We assume that the form model is stored in $form variable
// Render the whole form
$this->form()->render($form);

The same effect can be achieved with the __invoke magic method (see example below):
// The same, but with `__invoke`
$this->form($form);

7.13 Example: Creating the View Template for the


Contact Form
Now we are ready to define the presentation for our feedback form. If you remember, earlier we
added the contact-us.phtml view template in application/index/ directory under the modules
view/ directory. Replace the code in that file with the following:

Collecting User Input with Forms


1
2
3
4

228

<?php
$form = $this->form;
$form->prepare();
?>

5
6

<?php echo $this->form()->openTag($form); ?>

7
8
9
10

<?php echo $this->formLabel($form->get('email')); ?>


<?php echo $this->formElement($form->get('email')); ?>
<?php echo $this->formElementErrors($form->get('email')); ?>

11
12
13
14

<?php echo $this->formLabel($form->get('subject')); ?>


<?php echo $this->formElement($form->get('subject')); ?>
<?php echo $this->formElementErrors($form->get('subject')); ?>

15
16
17
18

<?php echo $this->formLabel($form->get('body')); ?>


<?php echo $this->formElement($form->get('body')); ?>
<?php echo $this->formElementErrors($form->get('body')); ?>

19
20

<?php echo $this->formElement($form->get('submit')); ?>

21
22

<?php echo $this->form()->closeTag(); ?>

As you can see from the code above, we do the following things to render the form:
In line 2, we access the $form variable passed from the controllers action.
In line 3, we call the Forms prepare() method to prepare the form for rendering. Please
note that calling this method is very important. If you forget to do that, there may be some
undesired rendering problems.
In line 6, we call the openTag() method of the Form view helper. Its purpose is to render the
opening <form> tag and its attributes. The method takes a single argument an instance
of the form model. Paired closing </form> tag is rendered in line 23 with the help of the
closeTag() method of the Form view helper.
In lines 8-10, we render the E-mail fields label, the text field itself and (possible) validation
errors with the help of the FormLabel, FormElement and FormElementErrors view helpers.
Those helpers take the instance of the form models element as a single argument. We get
an instance of the element with the get() method provided by the Form base class.
In lines 12-14, by analogy, we render the Subject field, its label and validation errors.
And in lines 16-18, we render the label, the field and the validation errors for the body text
area field.
In line 20, we render the Submit button.
When the view template renderer evaluates this code, it will produce the HTML output like
below:

Collecting User Input with Forms

229

<form action="/contact" method="post" name="contact-form">


<label for="email">Your E-mail</label>
<input type="text" name="email" id="email" value="">
<label for="subject">Subject</label>
<input name="subject" type="text" id="subject" value="">
<label for="body">Message Body</label>
<textarea name="body" id="body"></textarea>
<input name="submit" type="submit" value="Submit">
</form>

In the code above, we mostly used the FormElement, FormElementErrors and


FormLabel view helpers. You may use the generic FormRow or Form view helpers if you
want to reduce the amount of code to write, but this may result in less control of form
decoration.

If certain fields have validation errors, those errors will be outputted below the field in the form
of the <ul> unordered HTML list. For example, if you enter the 123@hostname into E-mail
form field, you would receive the following validation errors:
<label for="email">Your E-mail</label>
<input type="text" name="email" value="123@hostname">
<ul>
<li>&#039;hostname&#039; is not a valid hostname for the email address</li>
<li>The input does not match the expected structure for a DNS hostname</li>
<li>The input appears to be a local network name but local network names a\
re not allowed</li>
</ul>

7.13.1 Applying the Bootstrap CSS Styles to Form


The HTML markup above is missing CSS styling. What we want to achieve is to use Twitter
Bootstrap CSS classes to give the form a nice, professional-looking appearance. To add Bootstrap
styling to the form, you have to modify the code in the .phtml file to make it look like below:

Collecting User Input with Forms


1
2
3

<?php
$form = $this->form;
$form->prepare();

4
5
6
7
8

$form->get('email')->setAttributes(array(
'class'=>'form-control',
'placeholder'=>'name@example.com'
));

9
10
11
12
13

$form->get('subject')->setAttributes(array(
'class'=>'form-control',
'placeholder'=>'Type subject here'
));

14
15
16
17
18
19

$form->get('body')->setAttributes(array(
'class'=>'form-control',
'rows'=>6,
'placeholder'=>'Type message text here'
));

20
21
22

$form->get('submit')->setAttributes(array('class'=>'btn btn-primary'));
?>

23
24

<h1>Contact Us</h1>

25
26
27
28
29

<p>
Please fill out the following form to contact us.
We appreciate your feedback.
</p>

30
31
32
33

<div class="row">
<div class="col-md-6">
<?php echo $this->form()->openTag($form); ?>

34
35
36
37
38
39

<div class="form-group">
<?php echo $this->formLabel($form->get('email')); ?>
<?php echo $this->formElement($form->get('email')); ?>
<?php echo $this->formElementErrors($form->get('email')); ?>
</div>

40
41
42
43
44
45
46

<div class="form-group">
<?php echo $this->formLabel($form->get('subject')); ?>
<?php echo $this->formElement($form->get('subject')); ?>
<?php echo $this->formElementErrors($form->get('subject')); ?>
</div>

230

Collecting User Input with Forms


47
48
49
50
51

231

<div class="form-group">
<?php echo $this->formLabel($form->get('body')); ?>
<?php echo $this->formElement($form->get('body')); ?>
<?php echo $this->formElementErrors($form->get('body')); ?>
</div>

52
53

<?php echo $this->formElement($form->get('submit')); ?>

54
55
56
57

<?php echo $this->form()->closeTag(); ?>


</div>
</div>

In the code above, we added the .form-control CSS class to every input field in the form. We did
that with the setAttribute() method (see lines 5, 10 and 15). With that method, we also added
the placeholder attribute to define the nice-looking placeholder text when a field is empty. For
the body field, we added the rows attribute defining the height of the field (6 rows).
For the forms Submit button, we use the .btn and .btn-primary CSS classes (see line 21).
We also put label-input pairs inside of <div> elements with .form-group CSS class (lines 35, 41,
47).
We put a form inside of the 6-column-width grid cell, which makes the form half the width of
the screen (look at lines 31-32).

7.13.2 Styling the Validation Errors List


The error messages on your form, by default, look like a typical unordered list (<ul>). To give
them a nice visual appearance, we add a couple of CSS rules to the style.css file in APP_DIR/public
directory:
form ul {
list-style-type:none;
padding: 0px;
margin: 0px 5px;
}
form ul li {
color: red;
}

The CSS rules above will remove bullets from the list and make validation error messages appear
in red.

Collecting User Input with Forms

232

7.13.3 Adding the Thank You & Error Sending Email Pages
The last small thing we will do is preparing the view templates for the Thank You and Error
Sending Email pages.
Add the thank-you.phtml view template in application/index/ directory under the modules
view/ directory. Put the following HTML markup into the view template file:
<h1>Thank You!</h1>
<p>
<div class="alert alert-success">
We will respond to the E-mail address you have provided.
</div>
</p>

Next, add the send-error.phtml view template file. The HTML markup for the Error Sending
Email page is presented below:
<h1>Error Sending Email!</h1>
<p>
<div class="alert alert-warning">
Sorry, but we had an unexpected problem when trying to deliver
your message. Please try again later.
</div>
</p>

7.13.4 Results
Congratulations! Now, if you open the http://localhost/contactus URL in your web browser,
you should see a page like that shown in figure 7.19.
If you enter some invalid data in the form and click the Submit button, you should see the
validation errors (figure 7.20).
Entering the correct E-mail, subject and message text and submitting the form results in sending
the message and displaying the Thank You page (see figure 7.21).
On a sending failure, you will see the Error Sending Email page (see figure 7.22 for example):
You can see the Contact Us form in action in the Form Demo sample application bundled
with this book.

233

Collecting User Input with Forms

Figure 7.19. Contact Form

Figure 7.20. Form validation errors

234

Collecting User Input with Forms

Figure 7.21. Thank You page

Figure 7.22. Error Sending Email page

Collecting User Input with Forms

235

7.14 Summary
Forms are the way of collecting user-entered data on web pages. A form usually consists of
elements (input field + label pairs). Elements can optionally be grouped into fieldsets.
In a MVC-based web site, form functionality is separated into form models responsible for
element definition and validation, and form presentation implemented with the help of special
view helpers.
To create a form model, you write a class deriving from the Form base class. The form model is
initialized by adding its elements with the help of the base class-provided methods.
To submit form data to the server, the user clicks the Submit button, then the data is sent as
part of a HTTP request. Once the user submits the form, you can extract the form data in your
controller and ask the form model to validate it.
For checking and filtering the user-entered data, filters and validators are utilized. You use the
InputFilter class which is the container for validation rules.
If there are input errors, you display the form again, asking the user to correct the input errors.
If the data is correct, you process the data with your business logic layer.

8 Transforming Input Data with


Filters
In this chapter, we will provide an overview of standard filters that can be used with your forms.
A filter is a class which takes some input data, processes it, and produces some output data.
In general, you even can use filters outside forms to process an arbitrary data. For
example, filters may be used in a controller action to transform the data passed as GET
and/or POST variables to certain format.

ZF2 components covered in this chapter:


Component

Description

Zend\Filter

Contains various filters classes.

Zend\InputFilter

Implements a container for filters/validators.

8.1 About Filters


Filters are designed to take some input data, process it, and produce some output data. Zend
Framework 2 provides a lot of standard filters that can be used for creating filtering rules of your
forms (or, if you wish, to filter an arbitrary data outside of forms).

8.1.1 FilterInterface
Technically, a filter is a PHP class implementing the FilterInterface interface (it belongs to
Zend\Filter namespace). The interface definition is presented below:
1
2

<?php
namespace Zend\Filter;

3
4
5
6
7
8

interface FilterInterface
{
// Returns the result of filtering $value.
public function filter($value);
}

As you can see, the FilterInterface interface has the single method filter() (line 7), which
takes the single parameter $value. The method transforms the input data and finally returns the
resulting (filtered) value.

Transforming Input Data with Filters

237

A concrete filter class implementing the FilterInterface interface may have additional methods. For example, many filter classes have methods allowing to configure
the filter (set filtering options).

8.2 Standard Filters Overview


Standard filters implementing the FilterInterface interface belong to Zend\Filter component
. Filter class inheritance diagram is shown in figure 8.1. From that figure, you can see that base
concrete class for most standard filters is the AbstractFilter class, which in turn implements
the FilterInterface interface .
You may notice that there is a strange filter called StaticFilter which does not inherit
from AbstractFilter base class. This is because the StaticFilter class is actually a
wrapper (it is designed to be a proxy to another filter without explicit instantiation
of that filter).

Standard filters provided by the Zend\Filter component are listed in table 8.1 with a brief
description.
As you can see from the table, the standard filters can be roughly divided into the following
groups:
filters casting input data to a specified type (integer, boolean, date-time, etc.);
filters performing manipulations on a file path (getting the base name, parent directory
name, etc.);
filters performing compression and encryption of input data;
filters manipulating string data (case conversion, trimming, character replacement and
removal, URL normalizing, etc.);
proxy filters wrapping other filters (Callback, FilterChain and StaticFilter).
In this section, we only consider the standard filters belonging to the Zend\Filter namespace. In fact, there are other filters that can also be
considered as standard. For example, the Zend\Filter\File namespace contains several filters applicable to processing file uploads (those will
be covered in the next chapter). Additionally, the Zend\I18n component defines several filter classes that are aware of the users locale (those
filters will be covered in Chapter N).
From the figure 8.1, you may also notice that there are several more base filters: AbstractUnicode filter is the base class for the
StringToUpper and StringToLower filters, because it provides the string conversion functionality common to both of them. And, the Decompress
filter inherits from the Compress filter, because these filters are in fact very similar. By analogy, the Decrypt filter inherits from the Encrypt
filter, because they are the mirror reflection of each other as well.

238

Transforming Input Data with Filters

Figure 8.1. Filter class inheritance


Table 8.1. Standard filters

Class name

Description

Boolean

Returns a boolean representation of $value.

Int

Casts the input $value to int.

Digits

Returns the string $value, removing all but digit characters.

Null

Returns null if the input value can be treated as null; otherwise returns the
$value itself.
Takes a date & time string in an arbitrary format and produces a date & time
string in a given format.

DateTimeFormatter
BaseName

Given a string containing the path to a file or directory, this filter will return
the trailing name component.

Dir

Given a string containing the path of a file or directory, this filter will return
the parent directorys path.

RealPath

Returns canonicalized absolute pathname.

Compress

Compresses the input data with the specified algorithm (GZ by default).

239

Transforming Input Data with Filters

Table 8.1. Standard filters

Class name

Description

Decompress

Decompresses the input data with the specified algorithm (the effect is inverse
to the Compress filter).

Encrypt

Encrypts the input data with the specified cryptographic algorithm.

Decrypt

Decrypts the input data previously encrypted with the specified cryptographic
algorithm.

Inflector

Performs the modification of a word to express different grammatical


categories such as tense, mood, voice, aspect, person, number, gender and case.

PregReplace

Performs a regular expression search and replace.

StringToLower

Converts the string to lower case letters.

StringToUpper

Converts the string to upper case letters.

StringTrim

Removes white spaces (space, tabs, etc.) from the beginning and the end of the
string.

StripNewlines

Removes new line characters from string (ASCII codes #13, #10).

HtmlEntities

Returns the string, converting characters to their


corresponding HTML entity equivalents where they exist.

StripTags

Removes tags (e.g. <a></a>) and comments (e.g., <!-- -->).

UriNormalize

Converts an URL string to the normalized form and prepends the schema
part (e.g. converts www.example.com to http://www.example.com).

Callback

Allows to use a callback function as a filter.

FilterChain

Allows to organize several filters in a chain.

StaticFilter

Returns a value filtered through a specified filter class,


without requiring separate instantiation of the filter object.

8.3 Instantiating a Filter


In Zend Framework 2, you can use several methods of creating a filter:
instantiating it manually (with the new operator);
creating it with a factory class (by passing an array configuration); this way is used the
most frequently when adding filtering & validation rules in a form;
instantiating it implicitly with the StaticFilter wrapper class.
Next, we will cover these three methods in more details.

240

Transforming Input Data with Filters

8.3.1 Method 1: Instantiating a Filter Manually


As we previously said, a filter in general can be used not only with forms, but also for filtering
an arbitrary data. To do that, you simply create an instance of the filter class, configure the filter
by using the methods it provides, and call the filter() method on the filter.
For example, lets consider the usage of the StringTrim filter which removes the white space
characters from the beginning and the end of a string.
The StringTrim filter is useful for filtering user-entered string data (E-mail addresses,
user names, etc.), because site visitors tend to make typos in those data. For example,
a user may unintentionally enter a trailing space in an E-mail field, thus making an
E-mail invalid. With the StringTrim filter, you will easily cope with such input errors
and improve user experience.

The methods provided by the filter are listed in table 8.2:


Table 8.2. Public methods of the StringTrim filter

Method name

Description

__construct($charlistOrOptions)
filter($value)

Constructs the filter. Accepts the list of options.


Removes the predefined characters from the beginning and
the end of the string.
Defines the list of characters to strip off.
Returns the list of characters to strip off.

setCharList($charList)
getCharList()

As you can see from table, the StringTrim filter, additionally to the filter() method, provides
the constructor method which you can (optionally) pass with the complete list of options to
initialize the filter, and the setCharList() and getCharList() methods which can be used for
setting specific filter options.
All standard filters have the constructor method (optionally) accepting an array of
options for configuring the filter when instantiating it manually.

Below, we provide two code examples showing equivalent methods of manually creating an
instance of the StringTrim filter, setting its options and filtering a value.
Example 1. Passing options to the constructor method.

241

Transforming Input Data with Filters


1
2
3

<?php
// Optionally, define a short alias for the filter class name.
use Zend\Filter\StringTrim;

4
5
6

// Create an instance of the filter, passing options to the constructor.


$filter = new StringTrim(array('charlist'=>"\r\n\t "));

7
8
9

// Perform the trimming operation on the string.


$filteredValue = $filter->filter(' name@example.com

');

10
11

// The expected output of the filter is the 'name@example.com' string.

In the code above, we create the StringTrim filter object with the help of the new operator (line 6).
We pass the array of options to the constructor to set the list of characters the filter will remove
(here, we tell the filter to remove the new line characters, the tabulation character, and the space
character). Actually, passing the array of options to this filter can be omitted, because the filter
already has some default character list to strip off.
In line 9, we call the filter() method and pass it the string value name@example.com to be
trimmed. The expected output of this call is the name@example.com string.
Example 2. Without passing options to the constructor.
1
2
3

<?php
// Optionally, define a short alias for the filter class name.
use Zend\Filter\StringTrim;

4
5
6

// Create an instance of the filter.


$filter = new StringTrim();

7
8
9

// Specify which characters to remove.


$filter->setCharList("\r\n\t ");

10
11
12

// Perform the trimming operation on the string


$filteredValue = $filter->filter(' name@example.com

');

13
14

// The expected output of the filter is the 'name@example.com' string

In the code above, we create the StringTrim filter object with the help of the new operator (line
6).
In line 9, we (optionally) call the StringTrim filters setCharList() method to set the list of
characters the filter will remove (here, we tell the filter to remove the new line characters, the
tabulation character, and the space character). This call is optional, because the filter already has
some default character list for stripping off.
And, in line 12, we call the filter() method and pass it the string value name@example.com
to be trimmed. The expected output of this call is the name@example.com string.

Transforming Input Data with Filters

242

8.3.2 Method 2: Constructing a Filter with the StaticFilter


An alternative way of manual filter instantiation is by using the StaticFilter class. The
StaticFilter class is some kind of a proxy designed for automatic filter instantiation,
configuration and execution. For example, lets consider how to create the same StringTrim
filter, configure it and call its filter() method:
1
2
3
4

<?php
// Create and execute the StringTrim filter through the StaticFilter proxy.
$filteredValue = \Zend\Filter\StaticFilter::execute(' name@example.com ',
'StringTrim', array('charlist' => "\r\n\t "));

5
6

// The expected output of the filter is the 'name@example.com' string.

The StaticFilter class provides the execute() static method which takes three arguments: the
input value, the name of the filter to apply, and the array of filter-specific options.
In line 3, we call the execute() method to automatically create the StringTrim filter, call its
setCharList() method, and pass the input value to its filter() method. This is very useful,
because can be accomplished in a single line of code.

8.3.3 Method 3: Constructing a Filter From Array


When using filters with forms validation rules, you typically do not construct a filter object
explicitly as we did in the previous section, instead you pass an array configuration to the factory
class which automatically constructs the filter for you and (optionally) configures it. We already
saw how this works when adding validation rules for the feedback form in Chapter 7.
For example, lets show how to construct the same StringTrim filter with the help of the factory:
1
2
3

<?php
// It is assumed that you call the following code inside of the form model's
// addInputFilter() method.

4
5
6
7
8
9
10
11
12
13
14
15
16

$inputFilter->add(array(
// ...
'filters' => array(
array(
'name' => 'StringTrim',
'options' => array(
'charlist' => "\r\n\t "
)
),
),
// ...
);

Transforming Input Data with Filters

243

In the code above, we call the add() method provided by the InputFilter container class (line
5). The add() method takes an array which has the filters key. You typically register the filters
under that key (line 7). Filters registered under that key are inserted in a filter chain in the order
they appear in the list.
A filter configuration typically consists of the name (line 9) and options (line 10). The name is a
fully qualified filter class name (e.g. \Zend\Filter\StringTrim) or its short alias (StringTrim).
The options is an array consisting of filter-specific options. When the factory class instantiates
the filter, it passes the list of options to the filters constructor method, and the constructor
initializes the filter as needed.

8.4 About Filter Plugin Manager


In the previous example, you saw that you can use either the fully qualified filter class name or
its short alias when instantiating the filter from array. The short aliases for the standard filters
are defined by the FilterPluginManager class.
The FilterPluginManager class defines the short aliases for the standard filters.

A standard filters alias is typically the same as the class name. For example, the class
Zend\Filter\StringTrim has the short alias StringTrim.
The filter plugin manager is internally used by the InputFilter container class for instantiating
the standard filters.

8.5 Filters Behaviour in Case of Incorrect Input Data


Different filters behave differently if you pass it input data that the filter cannot process correctly.
Some filters (like Int filter) will process only scalar data. If you pass an array to such filter, it
will return the array as is.
Some filters can work with data in certain format only (e.g., with dates only). If filtering of
input data is impossible (for example, when you pass the filter some wrong data that it is
unable to process), the filter() method may throw a Zend\Form\Exception\RuntimeException
exception. This behaviour can be seen in DateTimeFormatter filter.
Some filters (e.g. Int or StringToLower) may rise a PHP warning if the value provided is in
incorrect format and cannot be filtered.
It is recommended to read filters documentation carefully to know what to expect of
the filter you plan to use in your form.

Transforming Input Data with Filters

244

8.6 Filter Usage Examples


Next, we will consider the usage of the most important standard filters. These describe the
methods (and options) a filter has, and provide code examples showing how to instantiate the
filter and apply it to input data. If you need to use a filter not covered in this section, please refer
to Standard Filters section of the Zend Framework 2 Reference Manual.

8.6.1 Filters Casting Input Data to a Specified Type


In this section, we will consider several filters from the group of filters related to casting input
data to the specified type and provide their usage examples.
8.6.1.1 Int Filter
The Int filter is a very simple filter that is designed to cast an arbitrary scalar data to an
integer number. This filter may be useful when adding validation rules for form fields that must
contain an integer numeric values (e.g. a drop-down list or a text field containing an amount of
something).
The Int class has the single filter() method.
The Int filter will not cast a non-scalar value. If you pass it an array, it will return it as
is.

Below, you can find a code example illustrating the usage of the Int filter.
1
2
3

<?php
// Create Int filter.
$filter = new \Zend\Filter\Int();

4
5
6
7

// Filter a value casting it to an integer number.


$filteredValue = $filter->filter('10'); // Returns (int) 10.
$filteredValue2 = $filter->filter(array('10', '20')); // Returns array as is.

In the code above, we pass the string 10 to the filter (line 6). The expected return value is the
integer 10.
In line 7, we pass an array to the filter. Because the Int filter works with scalar values only, it
returns the array as is (without changes) and raises a PHP warning.
8.6.1.2 Boolean Filter
The Boolean class is a filter that is designed to cast an arbitrary data to a boolean value (true or
false). This filter can be used for filtering check box form fields.
Its public methods are listed in table 8.3.

245

Transforming Input Data with Filters

Table 8.3. Public methods of the Boolean filter

Method name

Description

filter($value)
setCasting($flag)
getCasting()
setType($type)
getType()
setTranslations($translations)
getTranslations()

Returns a boolean representation of $value.


Sets casting flag.
Returns the casting flag.
Sets types from which to cast.
Returns types.
Sets translations.
Returns the translations.

The filter provides several methods allowing to set filtering options (setCasting(), setType(),
and setTranslations()).
The setCasting() method allows to choose one of two modes in which the filter to operate. If
the flag is true, the filter will behave like the PHP (boolean) cast operator. Otherwise (if the
flag is set to false) it will cast only from types defined by the setType() method, and all other
values will be returned as is.
The setType() filters method allows to define from which types to cast. This method accepts
the single argument $type which can be either an OR combination of TYPE_-prefixed constants,
or an array containing the literal equivalents of the constants. Possible constants accepted by the
setType() method and their literal equivalents are listed in table 8.4:
Table 8.4. Type constants

Constant

Numeric Value

Literal Equivalent

Description

TYPE_BOOLEAN
TYPE_INTEGER
TYPE_FLOAT
TYPE_STRING
TYPE_ZERO_STRING

1
2
4
8
16

boolean
integer
float
string
zero

TYPE_EMPTY_ARRAY
TYPE_NULL
TYPE_PHP

32
64
127

array
null
php

TYPE_FALSE_STRING

128

false

TYPE_LOCALIZED

256

localized

TYPE_ALL

511

all

Returns a boolean value as is.


Converts an integer 0 value to false.
Converts a float 0.0 value to false.
Converts an empty string to false.
Converts a string containing the
single character zero (0) to false.
Converts an empty array to false.
Converts a null value to false.
Converts values according to PHP
when casting them to boolean. (This
is the default behavior.)
Converts a string containing the
word false to a boolean false.
Converts a localized string which
contains certain word to boolean.
Converts all above types to boolean.

The following code example shows two equivalent ways you can call the setType() method:

Transforming Input Data with Filters

246

<?php
use Zend\Filter\Boolean;
// Call the setType() and pass it a combination of constants.
$filter->setType(Boolean::TYPE_BOOLEAN|
Boolean::TYPE_INTEGER|
Boolean::TYPE_STRING);
// Call the setType() and pass it an array with literal equivalents.
$filter->setType(array('boolean', 'integer', 'string'));

The setTranslations() method allows to define localized equivalents of boolean true and
false values. This method accepts the single parameter which must be an array in form of
keyvalue pairs, where the key is a localized string, and the value is its boolean representation.
The following code example shows how to use the setTranlsations() method:
<?php
$filter->setTranslations(array(
'yes' => true,
// English 'yes'
'no' => false,
// English 'no'
'ja' => true,
// German 'yes'
'nicht' => false, // German 'no'
'' => true,
// Russian 'yes'
'' => false
// Russian 'no'
));

Below, we provide a code example illustrating the usage of the Boolean filter.
<?php
// Create Boolean filter.
$filter = new \Zend\Filter\Boolean();
// Optionally configure the filter.
$filter->setCasting(true);
$filter->setType(\Zend\Filter\Boolean::TYPE_ALL);
$filter->setTranslations(array('yes'=>true, 'no'=>false));
// Filter a value casting it to a boolean number.
$filteredValue = $filter->filter('false'); // Returns boolean false.
$filteredValue2 = $filter->filter('1'); // Returns boolean true.
$filteredValue3 = $filter->filter('false'); // Returns boolean false.
$filteredValue4 = $filter->filter('yes'); // Returns boolean true.

247

Transforming Input Data with Filters

8.6.1.3 Null Filter


The Null filter is designed to cast an arbitrary data to a null value if it meets specific criteria.
This may be useful when you work with a database and want to have a null value instead of
any other type. If the value cannot be treated as null, the filter will return the value as is.
The Null filters public methods are listed in table 8.5.
Table 8.5. Public methods of the Null filter

Method name

Description

filter($value)
setType($type)
getType()

Casts the $value to null, if possible; otherwise returns values as is.


Defines from which types to cast.
Returns defined types.

By default, the Null filter behaves like PHPs empty() function: if the empty() function returns
a boolean true on the input data, then the filter will return the null value on that data, as well.
The setType() method can be used to set the type from which the filter will cast to null.
This method takes the single parameter which can either be a combination of TYPE_-prefixed
constants listed in table 8.6 or an array of their literal equivalents.
Table 8.6. Type constants

Constant

Numeric Value

Literal Equivalent

Description

TYPE_BOOLEAN

boolean

TYPE_INTEGER
TYPE_EMPTY_ARRAY
TYPE_STRING
TYPE_ZERO_STRING

2
4
8
16

integer
array
string
zero

TYPE_FLOAT
TYPE_ALL

32
63

float
all

Converts a boolean false value to


null.
Converts an integer 0 value to null.
Converts an empty array to null.
Converts an empty string to null.
Converts a string containing the
single character zero (0) to null.
Converts a float 0.0 value to null.
Converts all above types to null. This
is the default behaviour.

The following code example illustrates two equivalent ways you can call the setType() method:
<?php
use Zend\Filter\Null;
// Call the setType() and pass it a combination of constants.
$filter->setType(Null::TYPE_ZERO_STRING|Null::TYPE_STRING);
// Call the setType() and pass it an array with literal equivalents.
$filter->setType(array('zero', 'string'));

Below, a code example is provided showing how to use the Null filter:

248

Transforming Input Data with Filters

<?php
// Create Null filter.
$filter = new \Zend\Filter\Null();
// Optionally configure the filter.
$filter->setType(\Zend\Filter\Null::TYPE_ALL);
$filteredValue = $filter->filter('0'); // Returns null.
$filteredValue2 = $filter->filter('1'); // Returns string '1'.
$filteredValue3 = $filter->filter(false); // Returns null.

8.6.1.4 DateTimeFormatter Filter


The DateTimeFormatter filter accepts a date in an arbitrary format and converts it into the
desired format.
This filter can accept a string (e.g., 2014-03-22 15:36), an integer
timestamp (like the time() PHP function returns) or an instance of
the DateTime PHP class. The DateTimeFormatter filter may throw a
Zend\Filter\Exception\InvalidArgumentException exception if you pass it a
date in incorrect format.

Filters public methods are listed in table 8.7.


Table 8.7. Public methods of the DateTimeFormatter filter

Method name

Description

__construct($options)
filter($value)
setFormat($format)

Constructs the filter.


Transforms the date into desired format.
Sets the date format.

In the code example below, we show how to create the filter, pass it a string date, and convert it
to the desired format:
<?php
// Create DateTimeFormatter filter.
$filter = new \Zend\Filter\DateTimeFormatter();
// Set filter's format (optional).
$filter->setFormat('F j, Y g:i A');
// Transform the date to the specified format.
$filteredValue = $filter->filter('2014-03-22 15:36');
// The expected output is 'March 22, 2014 3:36 PM'.

Transforming Input Data with Filters

249

Internally, the DateTimeFormatter filter uses the DateTime class from the PHP standard
library for converting and formatting dates. For available date formats, please refer to
the PHP documentation for the DateTime class.

8.6.2 Filters Performing Manipulations on a File Path


In this section, we will consider usage examples of the filters from the group of filters related to
manipulating file paths.
8.6.2.1 BaseName Filter
The BaseName filter class is just a wrapper on the basename() PHP function. It takes a string
containing the path to a file or directory, and returns the trailing name component.
Below, you can find an example of the BaseName filter usage:
<?php
// Create BaseName filter.
$filter = new \Zend\Filter\BaseName();
// Filter a file path and return its last part.
$filteredValue = $filter->filter('/var/log/httpd/error.log');
// The expected filter's output is the 'error.log'.

The BaseName filter will not process a non-scalar value. If you pass it an array, it will
return the array as is and raise a PHP warning.

8.6.2.2 Dir Filter


The Dir filter class is just a wrapper on the dirname() PHP function. It takes a string containing
the path to a file or directory, and returns the the parent directorys path.
The Dir filter will not process a non-scalar value. If you pass it an array, it will return
the array as is.

Below, a code example demonstrating the usage of the Dir filter is provided.

Transforming Input Data with Filters

250

<?php
// Create Dir filter.
$filter = new \Zend\Filter\Dir();
// Filter a file path and return its directory name part.
$filteredValue = $filter->filter('/var/log/httpd/error.log');
// The expected filter's output is the '/var/log/httpd'.

8.6.2.3 RealPath Filter


The RealPath filter takes an absolute or a relative file path as a string input argument. It expands
all symbolic links and resolves references to /./, /../ and extra / characters in the input path
and returns the canonicalized absolute pathname.
The RealPath filter is a wrapper over the realpath() PHP function.

Filters public methods are listed in table 8.8.


Table 8.8. Public methods of the RealPath filter

Method name

Description

__construct($options)
filter($value)
setExists($flag)

Constructs the filter.


Returns canonicalized absolute pathname.
Specifies if the path must exist for this filter to succeed. The value true
means the path must exist; value false means a not existing path can be
given.
Returns true if the filtered path must exist.

getExists()

The RealPath filter returns a boolean false on failure, e.g. if the file does not exist. If non-existing
path is allowed, you can call the setExists() method with the false parameter.
Below, a code example demonstrating the usage of the RealPath filter is provided.
<?php
// Create RealPath filter.
$filter = new \Zend\Filter\RealPath();
// Filter a file path (it is assumed that the current
// working directory is /var/log/httpd and that it contains
// the error.log file).
$filteredValue = $filter->filter('./error.log');
// The expected filter's output is the '/var/log/httpd/error.log'.

251

Transforming Input Data with Filters

The RealPath filter will not process a non-scalar value. If you pass it an array, it will
return the array as is.

8.6.3 Filters Performing Compression and Encryption of Input


Data
In this section, we will consider several filters from the group of filters related to compressing
and encrypting the input data. These filters are not very usable for filtering form data, but can
be used outside of forms with a great success.
8.6.3.1 Compress Filter
The Compress filter is designed to compress input data with some compression algorithm. For
example, you can use this filter to compress the data and save it as an archive file.
Filters public methods are listed in table 8.9.
Table 8.9. Public methods of the Compress filter

Method name

Description

__construct($options)
filter($value)
getAdapter()
getAdapterName()
setAdapter($adapter)
getAdapterOptions()
setAdapterOptions($options)
getOptions($option)

Constructs the filter.


Performs data compression using the specified algorithm.
Returns the current adapter, instantiating it if necessary.
Retrieves adapter name.
Sets compression adapter.
Retrieves adapter options.
Sets adapter options.
Gets individual or all options from underlying adapter.

The Compress filter itself cannot compress data. Instead, it uses a so called adapter class. The
adapter class must implement the CompressionAlgorithmInterface interface. You attach an
adapter to the Compress filter, and the adapter implements the concrete compression algorithm.
There are several standard adapter classes available (see figure 8.2 and table 8.10 below). Those
classes live in Zend\Filter\Compress namespace.
Table 8.10. Compression adapters

Class name

Description

Bz2
Gz

Bzip2 (BurrowsWheeler) compression algorithm.


Gzip compression algorithm is based on Deflate algorithm, which is a combination of
LZ77 and Huffman coding.
ZIP compression algorithm widely used in Windows operating system.

Zip

http://www.bzip.org/
http://www.gzip.org/

252

Transforming Input Data with Filters

Table 8.10. Compression adapters

Class name

Description

Tar

Tarball file format is now commonly used to collect many files into one larger file for
archiving, while preserving file system information such as user and group
permissions, dates, and directory structures. Widely used in Linux operating system.
LZF is a very fast compression algorithm, ideal for saving space with only slight speed
cost.
Snappy is a fast data compression and decompression library developed by Google
based on ideas from LZ77.
RAR is an archive file format that supports data compression, error recovery and file
spanning.

Lzf
Snappy
Rar

Figure 8.2. Compression algorithm adapter inheritance

Below, a code example demonstrating the usage of the Compress filter is provided.

http://www.gnu.org/software/tar/tar.html
https://code.google.com/p/snappy/

Transforming Input Data with Filters


1
2
3

253

<?php
// Create Compress filter.
$filter = new \Zend\Filter\Compress();

4
5
6
7
8
9

// Configure the adapter.


$filter->setAdapter('Zip');
$filter->setAdapterOptions(array(
'archive' => 'example.zip',
));

10
11
12
13

// Compress an input data (it is assumed that you have the testfile.txt
// file in the current working directory.
$filter->filter('testfile.txt');

In the code above, we create the instance of the Compress filter (line 3), set its adapter (line 6),
set adapters options (line 7), and finally, compress the input file (line 13). As the expected result,
the example.zip archive file will be created in the current directory. The archive will contain the
testfile.txt file.
The Decompress filter is a mirror reflection of the Compress filter and can be used by
analogy. By that reason, we do not cover theDecompress filter in this section.

8.6.3.2 Encrypt Filter


The Encrypt filters purpose is encrypting the input data with the specified algorithm. Filters
public methods are listed in table 8.11.
Table 8.11. Public methods of the Encrypt filter

Method name

Description

__construct($options)
filter($value)
getAdapter()
setAdapter($adapter)

Constructs the filter.


Performs data compression using the specified algorithm.
Returns the current adapter, instantiating it if necessary.
Sets compression adapter.

The Encrypt filter uses adapter classes to perform actual data encryption. You attach an adapter
to the Encrypt filter with the setAdapter() method, and the adapter performs the concrete
encryption. An adapter class must implement the EncryptionAlgorithmInterface interface.
There are the following standard adapter classes are available (see figure 8.3 below). Those classes
live in Zend\Filter\Encrypt namespace.
BlockCipher implements symmetric block cipher algorithm.
Openssl uses an encryption algorithm from the OpenSSL library.

254

Transforming Input Data with Filters

Figure 8.3. Encryption algorithm adapter inheritance

Below, a code example demonstrating the usage of the Encrypt filter is provided.
1
2
3

<?php
// Create Encrypt filter.
$filter = new \Zend\Filter\Encrypt();

4
5
6

// Set encryption adapter.


$filter->setAdapter('BlockCipher');

7
8
9

// Encrypt an input data.


$filteredValue = $filter->filter('some data to encrypt');

The expected result is string encrypted with the block cipher.


The Decrypt filter is a mirror reflection of the Encrypt filter and can be used by
analogy. By that reason, we do not cover the Decrypt filter in this section.

8.6.4 Filters Manipulating String Data


In this section, we will consider usage examples of the filters from the group of filters related to
manipulating string data.
8.6.4.1 StringToLower Filter
The StringToLower filter class is designed for converting the input string data to lower-case
letters. The public methods of the filter are provided in table 8.12 below.
Table 8.12. Public methods of the StringToLower filter

Method name

Description

__construct($options)
filter($value)
setEncoding($encoding)
getEncoding()

Constructs the filter.


Converts the string to lower-case letters.
Sets the input encoding for the given string.
Returns the encoding.

Transforming Input Data with Filters

255

By default, the filter behaves like the strtolower() PHP function. Given a string, it returns
the string with all alphabetic characters converted to lower-case. The alphabetic characters
are determined by the system locale. This means that in, for example, the default C locale,
characters such as umlaut-A () will not be converted.
Calling the setEncoding() method on the filter and passing it an encoding to use, forces this filter
to behave like the mb_strtolower() PHP function. By contrast to strtolower(), alphabetic
is determined by the Unicode character properties. Thus the behaviour of this function is not
affected by locale settings and it can convert any characters that have alphabetic property,
such as A-umlaut ().
If the value provided is non-scalar, the value will remain unfiltered and an E_USER_WARNING will be raised indicating its cannot be filtered.

Below, a code example is provided showing how to use the StringToLower filter:
<?php
// Create StringToLower filter.
$filter = new \Zend\Filter\StringToLower();
// (Optionally) set encoding on the filter.
$filter->setEncoding('UTF-8');
// Filter a string.
$filteredValue = $filter->filter('How to Start a Business in 10 Days');
// The expected filter's output is the 'how to start a business in 10 days'.

The StringToUpper filter (converting a string to upper-case letters) is a mirror


reflection of the StringToLower filter and can be used by analogy. By that reason,
we do not cover the StringToUpper filter in this section.

8.6.4.2 PregReplace Filter


The PregReplace filter can be used for performing a regular expression search and replace in a
string data. This filter is a wrapper over the preg_replace() PHP function. The public methods
of the filter are provided in table 8.13 below.

256

Transforming Input Data with Filters

Table 8.13. Public methods of the PregReplace filter

Method name

Description

__construct($options)
filter($value)
setPattern($pattern)

Constructs the filter.


Performs a regular expression search and replace.
Sets the pattern to search for. It can be either a string or an array
with strings.
Returns the pattern.
The string or an array with strings to replace.
Get currently set replacement value.

getPattern()
setReplacement($replacement)
getReplacement()

Below, a code example is provided showing how to use the StringToLower filter:
<?php
// Create PregReplace filter.
$filter = new \Zend\Filter\PregReplace();
// Configure the filter.
$filter->setPattern("/\s\s+/");
$filter->setReplacement(' ');
// Filter a string.
$filteredValue = $filter->filter('An example

with

multiple

spaces.');

// The expected filter's output is the 'An example with multiple spaces.'

8.6.4.3 StripTags Filter


The StripTags filter removes all tags (e.g. <!-- -->, <p>, <h1> or <?php ?>) from the input
string. It allows to explicitly define the tags which should not be stripped out. Additionally, it
provides an ability to specify which attributes are allowed across all allowed tags and/or specific
tags only.
Public methods of the StripTags filters are listed in table 8.14.
Table 8.14. Public methods of the StripTags filter

Method name

Description

__construct($options)
filter($value)
getAttributesAllowed()
setAttributesAllowed($attributesAllowed)
getTagsAllowed()
setTagsAllowed($tagsAllowed)

Constructs the filter.


Returns the value with tags stripped off it.
Returns the list of attributes allowed for the tags.
Sets the list of attributes allowed for the tags.
Returns the list of tags allowed.
Sets the list of tags allowed.

Below, a code example is provided showing how to use the StripTags filter:

Transforming Input Data with Filters

257

<?php
// Create StripTags filter.
$filter = new \Zend\Filter\StripTags();
// Configure the filter.
$filter->setTagsAllowed(array('p'));
// Filter a string.
$filteredValue = $filter->filter('<p>Please click the following <a href="examp\
le.com">link</a>.</p>');
// The expected filter's output is the '<p>Please click the following link.</p\
>;'

The StripTags will not process a non-scalar value. If the value passed to the filter is
non-scalar, the value will remain unfiltered.

8.6.4.4 StripNewlines Filter


The StripNewlines filter is a very simple filter which returns the input string without any
newline control characters (\r, \n).
Below, a code example is provided showing how to use the StripNewlines filter:
<?php
// Create StripNewlines filter.
$filter = new \Zend\Filter\StripNewlines();
// Filter a string.
$filteredValue = $filter->filter("A multi line\r\n string");
// The expected filter's output is the 'A multi line string'.

The StripNewlines will not process a non-scalar value. If the value passed to the filter
is non-scalar, the value will remain unfiltered.

8.6.4.5 UriNormalize Filter


The UriNormalize filter can be used for normalizing an URL sting and (optionally) applying a
scheme part to it. The public methods of the filter are provided in table 8.15 below.

258

Transforming Input Data with Filters

Table 8.15. Public methods of the UriNormalize filter

Method name

Description

filter($value)

Filter the URL by normalizing it and applying a default


scheme if set.
Set the default scheme to use when parsing scheme-less
URIs.
Set a URI scheme to enforce on scheme-less URIs.

setDefaultScheme($defaultScheme)
setEnforcedScheme($enforcedScheme)

The URL normalization procedure typically consists of the following steps:


1. The URL string is decomposed into its schema, host, port number, path and query parts.
If the scheme part is missing from the original URL, the default scheme is used.
2. The scheme and host parts are converted to lowercase letters.
3. The port number is checked against the list of allowed port numbers, and if doesnt belong
to the list, the port number is cleared.
4. The path part of the URL is filtered, removing redundant dot segments, URL-decoding any
over-encoded characters and URL-encoding everything that needs to be encoded and is
not.
5. The query part is sanitized, URL-decoding everything that doesnt need to be encoded,
and URL-encoding everything else.
The URL normalization procedure rules may be different for different protocols (schemes). If
the URL doesnt contain the scheme part, the http scheme is assumed by default. You may
use the UriNormalize filters setDefaultScheme() method to set the default scheme for URL
normalization. It accepts any of the following schemes: http, https, file, mailto, urn and tag.
Additionally, the UriNormalize filters setEnforcedScheme() allows to override the default
scheme part by the so called enforced scheme, if the original URL doesnt contain scheme
part.
Below, a code example is provided showing how to use the UriNormalize filter:
<?php
// Create UriNormalize filter.
$filter = new \Zend\Filter\UriNormalize();
// Configure the filter.
$filter->setDefaultScheme('http');
$filter->setEnforcedScheme('https');
// Filter an URL string.
$filteredValue = $filter->filter('www.example.com');
// The expected filter's output is the 'https://www.example.com/'

259

Transforming Input Data with Filters

8.6.5 Organizing Filters in a Chain


Filters can be organized in a sequence. This is accomplished by the FilterChain class. When
such a compound filter is run, the value filtered by the first filter is passed as an input for the
second one, then the value filtered by the second filter will be passed to the third one, and so on.
The FilterChain class is internally used by the InputFilter container class for storing
the sequence of filters attached to the form models field.

Public methods provided by the FilterChain class are presented in table 8.16:
Table 8.16. Public methods of the FilterChain filter

Method name

Description

filter($value)

Returns value filtered through each filter in the


chain. Filters are run in the order in which they
were added to the chain (FIFO).
Sets options.
Attaches an existing filter instance (or a callback
function) to the chain.
Instantiates the filter by class name or alias and
inserts it into the chain.
Merges the filter chain with another filter chain.
Returns all the attached filters.
Returns the count of attached filters.

setOptions($options)
attach($callback, $priority)
attachByName($name, $options, $priority)
merge($filterChain)
getFilters()
count()

An example filter chain is shown in figure 8.4. It consists of the StringTrim filter followed by
the StripTags filter, which in turn is followed by the StripNewLines filter.

Figure 8.4. Filter chain

To construct the filter chain like in figure 8.4, we can use the following code:

Transforming Input Data with Filters

260

<?php
use Zend\Filter\FilterChain;
// Instantiate the filter chain.
$filter = new FilterChain();
// Insert filters into filter chain.
$filter->setOptions(array('filters'=>array(
array('name'=>'StringTrim',
'options'=>array('charlist'=>"\r\n\t "),
'priority'=>FilterChain::DEFAULT_PRIORITY
),
array('name'=>'StripTags',
'options'=>array('tagsallowed'=>array('p')),
'priority'=>FilterChain::DEFAULT_PRIORITY
),
array('name'=>'StripNewLines',
'priority'=>FilterChain::DEFAULT_PRIORITY
)
)
));
// Execute all filters in the chain.
$filteredValue = $filter->filter(" name@example.com<html>\n ");
// The expected output is 'name@example.com'.

In the code above, we instantiate the FilterChain filter with the new operator (line 5). In line 8,
we set construct the chain of filters with the setOptions() method.
The method takes an array configuration which looks the same way as in InputFilters add()
method. The array has filters key where you register the filters you want to insert into the chain.
For each attached filter, you provide the following subkeys: * name is the fully qualified class
name of the filter (e.g. ZendFilterStringTrim) or its short alias (e.g. StringTrim); * options is
an array of options passed to the filter; * priority is the optional key which defines the priority
of the filter in the chain. Filters with higher priority are visited first. The default value for the
priority is DEFAULT_PRIORITY.
Finally, in line 20, we call the filter() method, which walks through the chain and passes the
filtered value to each filter in turn.

8.6.6 Custom Filtering with the Callback Filter


Standard filters are designed to be used in frequently appearing situations. For example, you
may often need to trim a string or convert it to a lower case. However, sometimes there are cases
where you cannot use a standard filter. Here, the Callback filter will be handy.

261

Transforming Input Data with Filters

The Callback filter is designed as a wrapper for your custom filtering algorithm. For example,
this may be useful when a standard filter is not suitable, and you need to apply your own filtering
algorithm to the data.
You implement your custom filtering algorithm as a callback function or a callback
class method. A callback is a function or a public method of a class which is called by
the Callback filter and is passed the value to be filtered and, optionally, user-defined
argument(s).

The public methods provided by the Callback filter are listed in table 8.17.
Table 8.17. Public methods of the Callback filter

Class name

Description

filter($value)
setCallback($callback)
getCallback()
setCallbackParams($params)
getCallbackParams()

Executes a callback function as a filter.


Sets a new callback for this filter.
Returns callback set for the filter.
Sets parameters for the callback.
Get parameters for the callback.

As you can see from the table, the Callback filter provides the setCallback() and setCallbackParams()
methods that can be used to set the callback function (or the callback class method) and optionally - pass it one or several parameters.
8.6.6.1 Example
To demonstrate the usage of the Callback filter, lets add the phone number field to our
ContactForm form model class and attach a custom filter to it.
An international phone number typically looks like 1 (808) 456-7890. It consists of the country
code followed by the three-digit area code enclosed into braces. The rest of the phone consists of
seven-digits subscriber code divided in two groups separated by a dash character. The country
code, the area code and the subscriber code are separated by the space character. We will refer
to this phone format as to international format.
The international phone format is required for making telephone calls between different
countries (or areas). If the calls are made within the same area, the telephone number may simply
look like 456-7890 (we just omit the country code and area code). We will refer to this phone
format as to local phone format.
To make our filter as generic as possible, we assume that for some forms, we may require that the
user to enter the phone in international format, and for others to enter the phone in local format.
Because some site visitors may enter their phone number with the deviation of the required
format, we want to apply the filter that will normalize the phone number for us.
To do the phone normalization, the filter will:
1. Strip out any non numeric characters of the input value.

Transforming Input Data with Filters

262

2. If there are too few digits, it will pad the digits to the required length.
3. Add the braces, spacing and the dash (when using the international format); or simply add
the dash (when using the local format).
Because ZF2 does not provide a standard filter for accomplishing such phone filtering operation,
we will use the Callback wrapper filter. To do that, we will make the following changes to the
code of our ContactForm class:
1
2
3
4
5
6
7

<?php
// ...
class ContactForm extends Form
{
// ...
protected function addElements() {
// ...

// Add "phone" field


$this->add(array(
'type' => 'text',
'name' => 'phone',
'attributes' => array(
'id' => 'phone'
),
'options' => array(
'label' => 'Your Phone',
),
));

9
10
11
12
13
14
15
16
17
18
19
20

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

private function addInputFilter() {


// ...
$inputFilter->add(array(
'name'
=> 'phone',
'required' => true,
'filters' => array(
array(
'name' => 'Callback',
'options' => array(
'callback' => array($this, 'filterPhone'),
'callbackParams' => array(
'format' => 'intl'
)
)
),
),
)

Transforming Input Data with Filters

263

);

39

40
41

// Custom filter for a phone number.


public function filterPhone($value, $format) {

42
43
44

if(!is_scalar($value)) {
// Return non-scalar value unfiltered.
return $value;
}

45
46
47
48
49

$value = (string)$value;

50
51

if(strlen($value)==0) {
// Return empty value unfiltered.
return $value;
}

52
53
54
55
56

// First remove any non-digit character.


$digits = preg_replace('#[^0-9]#', '', $value);

57
58
59

if($format == 'intl') {
// Pad with zeros if count of digits is incorrect.
$digits = str_pad($digits, 11, "0", STR_PAD_LEFT);

60
61
62
63

// Add the braces, spacing and the dash.


$phoneNumber = substr($digits, 0, 1) . ' ('.
substr($digits, 1, 3) . ') ' .
substr($digits, 4, 3) . '-'.
substr($digits, 7, 4);
} else { // 'local'
// Pad with zeros if count of digits is incorrect.
$digits = str_pad($digits, 7, "0", STR_PAD_LEFT);

64
65
66
67
68
69
70
71
72

// Add the the dash.


$phoneNumber = substr($digits, 0, 3) . '-'. substr($digits, 3, 4);

73
74

75
76

return $phoneNumber;

77

78
79

In lines 10-19 of the code above, we add the phone field to the ContactForm form model. The
field is a usual text input field, and we already had some experience of working with such fields
earlier.
Then, in lines 24-39, we add a validation rule for the phone field of our form. Under the filters

Transforming Input Data with Filters

264

key (line 27), we register the Callback filter (here, we use the short alias Callback, but you can
alternatively use the fully qualified class name \Zend\Filter\Callback).
The filter takes two options (line 30): the callback option and the callback_params option. The
callback option is an array consisting of two elements, which represent the class and method to
call, respectively. In this example, the callback is the filterPhone() method of the ContactForm
class. We pass the format parameter to the callback method with the help of callbackParams
option (line 32).
In lines 43-79, we define the filterPhone() callback method, which takes two arguments: the
$value is the phone number to filter, and the $format is the desired phone number format. The
$format parameter may either be local (local format) or intl (for international format).
In the filterPhone() callback method, we do the following:
First, in line 45, we check if the $value parameter is a scalar, and not an array. If the value
is not a scalar, we return it without change.
In line 52, we check the input values length. We do nothing if user entered empty phone
number, we just return it as is.
Then, we remove any non-digit characters (line 58).
If phone length is too short, we pad it with zeroes.
We add the braces, the dash, and space characters (for international phones), and the dash
(for local phone numbers).
Finally, we return the resulting phone number.
To see how this filter works, you can open the http://localhost/contactus URL in your web
browser. If you enter some phone number in an incorrect format, the filter will fix the phone
number and transform it to the desired format.

8.7 Writing Own Filter


An alternative of using the Callback filter is writing your own filter class implementing the
FilterInterface interface. Then, this filter may be used in forms of your web application (or, if
you wish, outside a form).
To demonstrate how to create your own filter, we will write the PhoneFilter class encapsulating
the phone filtering algorithm we used with the Callback filter example.
As you might remember, the base concrete class for all standard filters is the
AbstractFilter class. By analogy, we will also derive our custom PhoneFilter filter
from that base class.

We plan to have the following methods in our PhoneFilter filter class (see table 8.18):

Transforming Input Data with Filters

265

Table 8.18. Public methods of the PhoneFilter filter

Method name

Description

__construct($options)

Constructor. Accepts an optional argument $options which is needed to


set filter options at once.
Sets the phone format option.
Returns the phone format option.
Runs the phone filter.

setFormat($format)
getFormat()
filter($value)

To start, create the PhoneFilter.php file in the Application/Service directory under the modules
source directory . Put the following code into that file:
1
2

<?php
namespace Application\Service;

3
4

use Zend\Filter\AbstractFilter;

5
6
7
8
9
10
11
12

// This filter class is designed for transforming an arbitrary phone number to


// the local or to the international format.
class PhoneFilter extends AbstractFilter
{
// Phone format constants
const PHONE_FORMAT_LOCAL = 'local'; // Local phone format
const PHONE_FORMAT_INTL = 'intl'; // International phone format

13
14
15
16
17

// Available filter options.


protected $options = array(
'format' => self::PHONE_FORMAT_INTL
);

18
19
20
21
22
23

// Constructor.
public function __construct($options = null)
{
// Set filter options (if provided).
if(is_array($options)) {

24

if(isset($options['format']))
$this->setFormat($options['format']);

25
26

27
28

29
30
31
32

// Sets phone format.


public function setFormat($format)
{
The PhoneFilter class may be considered as a service model, because its goal is to process data, not to store it.

Transforming Input Data with Filters

// Check input argument.


if( $format!=self::PHONE_FORMAT_LOCAL &&
$format!=self::PHONE_FORMAT_INTL ) {
throw new \Exception('Invalid format argument passed.');
}

33
34
35
36
37
38

$this->options['format'] = $format;

39
40

41
42
43
44
45
46

// Returns phone format.


public function getFormat()
{
return $this->format;
}

47
48
49
50
51
52
53
54

// Filters a phone number.


public function filter($value)
{
if(!is_scalar($value)) {
// Return non-scalar value unfiltered.
return $value;
}

55
56

$value = (string)$value;

57
58
59
60
61

if(strlen($value)==0) {
// Return empty value unfiltered.
return $value;
}

62
63
64

// First remove any non-digit character.


$digits = preg_replace('#[^0-9]#', '', $value);

65
66

$format = $this->options['format'];

67
68
69
70

if($format == self::PHONE_FORMAT_INTL) {
// Pad with zeros if count of digits is incorrect.
$digits = str_pad($digits, 11, "0", STR_PAD_LEFT);

71
72
73
74
75
76
77
78

// Add the braces, spacing and the dash.


$phoneNumber = substr($digits, 0, 1) . ' (' .
substr($digits, 1, 3) . ') ' .
substr($digits, 4, 3) . '-' .
substr($digits, 7, 4);
} else { // self::PHONE_FORMAT_LOCAL
// Pad with zeros if count of digits is incorrect.

266

Transforming Input Data with Filters

267

$digits = str_pad($digits, 7, "0", STR_PAD_LEFT);

79
80

// Add the the dash.


$phoneNumber = substr($digits, 0, 3) . '-'. substr($digits, 3, 4);

81
82

83
84

return $phoneNumber;

85

86
87

From line 2, you can see that the filter class lives in the Application\Service namespace.
In line 8, we define the PhoneFilter class. We derive our filter class from the AbstractFilter
base class to reuse the functionality it provides. Line 4 contains the short alias for the AbstractFilter
class.
In lines 11-12, for convenience, we define the phone format constants (PHONE_FORMAT_INTL for
international format and PHONE_FORMAT_LOCAL for local format). These are the equivalents of the
intl and local strings, respectively.
In lines 15-17, we define the $options private variable which is an array having the single key
named format. This key will contain the phone format option for our filter.
In lines 20-28, we have the constructor method which takes the single argument $options.
When constructing the filter manually, you may omit this parameter. But, when the filter is
constructed by the factory class, the factory will pass filter options to filters constructor through
this argument.
In lines 31-37 and 43-46, we have the setFormat() and getFormat() methods that allow to set
and retrieve the current phone format, respectively.
In lines 49-86, we have the filter() method. This method encapsulates the phone number
filtering algorithm. It takes the $value parameter, transforms it taking the selected phone format
in account, and returns the formatted phone number.

8.7.1 Using the PhoneFilter Class


When the PhoneFilter filter class is ready, you can easily start using it in the feedback form
(or in another form) as follows. Iit is assumed that you call the following code inside of the
ContactForm::addInputFilter() method:

Transforming Input Data with Filters

268

$inputFilter->add(array(
'name'
=> 'phone',
'required' => true,
'filters' => array(
array(
'name' => '\Application\Service\PhoneFilter',
'options' => array(
'format' => \Application\Service\PhoneFilter::PHONE_FORMAT_INTL
)
),
// ...
),
// ...
)
);

You can see how the PhoneFilter filter works in the Form Demo sample application bundled
with this book. Open the http://localhost/contactus page in your web browser. If you enter
some phone number in an incorrect format, the filter will fix the phone number.
If you wish, you can use the PhoneFilter outside of forms, as shown in code example below:
<?php
use Application\Service\PhoneFilter;
// Create PhoneFilter filter
$filter = new PhoneFilter();
// Configure the filter.
$filter->setFormat(PhoneFilter::PHONE_FORMAT_INTL);
// Filter a string
$filteredValue = $filter->filter('12345678901');
// The expected filter's output is the '1 (234) 567-8901'

8.8 Summary
Filters are designed to take some input data, process it, and produce some output data. Zend
Framework 2 provides a lot of standard filters that can be used for creating filtering rules of your
forms (or, if you wish, to filter an arbitrary data outside of forms).
The standard filters can be roughly divided into several groups:
filters casting input data to a specified type;
filters performing manipulations on a file path;

Transforming Input Data with Filters

269

filters performing compression and encryption of input data;


filters manipulating string data;
and proxy filters wrapping other filters.
If a standard filter is not suitable, it is possible to create a custom filter class. In this chapter, we
have provided an example of how to write own PhoneFilter class capable of filtering phone
numbers.

9 Checking Input Data with


Validators
In this chapter, we will provide an overview of standard validators that can be used with your
forms. A validator is a class designed to take some input data, check it for correctness, and
return a boolean result telling whether the data is correct (and error messages if the data has
some errors).
In general, you even can use validators outside forms to process an arbitrary data. For
example, validators may be used in a controller action to ensure that data passed as
GET and/or POST variables is secure and conform to certain format.

ZF2 components covered in this chapter:


Component

Description

Zend\Validator

Implements various validator classes.

Zend\InputFilter

Implements a container for filters/validators.

9.1 About Validators


A validator is designed to take some input data, check it for correctness, and return a boolean
result telling whether the data is correct. If the data is incorrect, the validator generates the list
of errors describing why the check didnt pass.

9.1.1 ValidatorInterface
In ZF2, a validator is a usual PHP class which implements the ValidatorInterface interface (it
belongs to Zend\Validator namespace). The interface definition is presented below:
1
2

<?php
namespace Zend\Validator;

3
4
5
6
7

interface ValidatorInterface
{
// Returns true if and only if $value meets the validation requirements.
public function isValid($value);

8
9

// Returns an array of messages that explain why

Checking Input Data with Validators

// the most recent isValid() call returned false.


public function getMessages();

10
11
12

271

As you can see, the ValidatorInterface has two methods: the isValid() method (line 7) and
getMessages() method (line 11).
The first one, isValid() method, is intended to perform the check of the input value (the $value
parameter). If the validation of the $value passes, the isValid() method returns boolean true.
If the $value fails validation, then this method returns false.
A concrete validator class implementing the ValidatorInterface interface may have
additional methods. For example, many validator classes have methods allowing to
configure the validator (set validation options).

9.2 Standard Validators Overview


Standard ZF2 validators are provided by the Zend\Validator component . Standard validator
classes inheritance is shown in figure 9.1. As you can see from the figure, most of them are
derived from AbstractValidator base class.
Standard validators together with their brief description are listed in table 9.1. As you may notice
from the table, they can be roughly divided into several groups:
validators for checking value conformance to certain format (IP address, host name, E-mail
address, credit card number, etc.);
validators for checking if a numerical value lies in a given range (less than, greater than,
between, etc.);
validators working as proxies to other validators (ValidatorChain, StaticValidator
and Callback).
Here, we only consider the standard validator classes belonging to the Zend\Validator namespace. But, actually there are more validators
that can be considered as standard. We will cover them in further chapters.

272

Checking Input Data with Validators

Figure 9.1. Validator class inheritance


Table 9.1. Standard validators

Class name
EmailAddress

Description
Returns boolean true if the value is a valid E-mail address; otherwise returns
false.

Hostname

Checks whether the value is a valid host name.

Barcode

Returns boolean true if and only if the value contains a valid barcode.

CreditCard

Returns true if and only if the value follows the common format of credit card
number (Luhn algorithm, mod-10 checksum).

Iban

Returns true if the value is a valid International Bank Account Number


(IBAN); otherwise returns false.

273

Checking Input Data with Validators

Table 9.1. Standard validators

Class name

Description

Isbn

Returns boolean true if and only if value is a valid International Standard


Book Number (ISBN).

Ip

Returns true if value is a valid IP address; otherwise returns false.

Uri

Returns true if and only if the value is an Uniform Resource Identifier (URI).

Between

Returns true if the value lies in certain range; otherwise returns false.

LessThan
GreaterThan

Returns boolean true if the value is less than certain number; otherwise
returns false.
Returns true if and only if value is greater than certain number.

Identical

Returns boolean true if a the value matches a given token.

Step

Checks whether the value is a scalar and a valid step value.

Csrf

This validator checks if the provided token matches the one previously
generated and stored in a PHP session.

Date

Returns true if value is a valid date of the certain format.

DateStep

Returns boolean true if a date is within a valid step.

InArray

Returns true if value is contained in the given array; otherwise returns false.

Digits

Returns boolean true if and only if $value only contains digit characters.

Hex

Returns true if and only if value contains only hexadecimal digit characters.

IsInstanceOf

Returns true if value is instance of certain class; otherwise returns false.

NotEmpty

Returns true if value is not an empty value.

Regex

Returns true if value matches against given pattern; otherwise returns false.

StringLength

Returns true if the string length lies within given range.

Explode

Splits the given value in parts and returns true if all parts pass the given check.

StaticValidator

This validator allows to execute another validator without explicitly


instantiating it.

Callback

This validator allows to execute a custom validation algorithm through the


user-provided callback function.

ValidatorChain

Wrapper validator allowing to organize several validators in a chain. Attached


validators are run in the order in which they were added to the chain (FIFO).

9.3 Validator Behaviour in Case of Invalid or


Unacceptable Data
If you pass a validator some data that doesnt pass the check, the validator internally creates the
list of error messages that can be retrieved with the getMessages() method. For example, look

Checking Input Data with Validators

274

below for possible validation errors that the EmailValidator returns if you pass it the abc@ewr
value:
array(3) {
["emailAddressInvalidHostname"] =>
string(51) "'ewr' is not a valid hostname for the email address"
["hostnameInvalidHostname"] =>
string(66) "The input does not match the expected structure for a DNS host\
name"
["hostnameLocalNameNotAllowed"] =>
string(84) "The input appears to be a local network name but local network\
names are not allowed"
}

Validators getMessages() method will return an array of messages that explain why the
validation failed. The array keys are validation failure message identifiers, and the array values
are the corresponding human-readable message strings.
If isValid() method was never called or if the most recent isValid() call returned true, then
the getMessages() method returns an empty array. Also, when you call isValid() several times,
the previous validation messages are cleared, so you see only validation errors from the last call.
Some validators may work with input data in certain format only (for example, a validator may
require that the input data be a string, but not an array). If you pass it data in unacceptable
format, the validator may throw an Zend\Validator\Exception\RuntimeException exception
or raise a PHP warning.
It is recommended to check certain validators documentation to be aware of its actual
behaviour in case of unacceptable data.

9.4 Instantiating a Validator


In Zend Framework 2, there are several methods of creating a validator:
instantiating it manually (with the new operator);
creating it with a factory class (by passing an array configuration); this way is used the
most frequently when adding validation rules in a form;
instantiating it implicitly with the StaticValidator wrapper class.
Next, we will cover these three methods in more details.

275

Checking Input Data with Validators

9.4.1 Method 1. Manual Instantiation of a Validator


A validator in general can be used not only with forms, but also for validation of an arbitrary
data. In order to do that, you simply create an instance of the validator class, configure the
validator by using the methods it provides, and call the isValid() method on the validator.
For example, lets consider the usage of the EmailAddress validator which checks an E-mail
address for conformance to RFC-2822 standard. An E-mail address typically consists of the
local part (user name) followed by the at character (@), which is in turn followed by the host
name. For example, in the name@example.com E-mail address, name is the local part, and
example.com is the host name.
The EmailAddress validator is useful for checking an user-entered E-mail addresses
on your forms for correctness. The validator will check for the correctness of the local
part and the host name, for presence of the at character (@) and, optionally, will
connect to the recipients host and query the DNS service for existence of the MX
(Mail Exchanger) record .

The methods provided by the EmailAddress validator are listed in table 9.2:
Table 9.2. Public methods of the EmailAddress validator

Method name

Description

__construct($options)

Constructs the validator. Accepts the list of options


allowing to configure it.
Returns true if the value is a valid E-mail address
according to RFC-2822; otherwise returns false.
If validation failed, this method will return an array
of error messages.
Tells the validator to check the host name part for
correctness.
Returns true if host name part check is enabled.
Attaches the validator to use for checking host
name part of the E-mail address.
Returns the validator used for checking host name
part of the E-mail address.
Sets the allowed types of host names to be used in
an E-mail address.
Returns the allowed types of host names.
Sets whether to perfrom the check for a valid MX
record via DNS service.
Returns true if MX check mode is enabled.
Sets whether to use deep validation for MX records.
Returns true if the deep MX check mode is enabled;
otherwise returns false.
Returns true if MX checking via getmxrr() PHP
function is supported in the system; otherwise
returns false.

isValid($value)
getMessages()
useDomainCheck($domain)
getDomainCheck()
setHostnameValidator($hostnameValidator)
getHostnameValidator()
setAllow($allow)
getAllow()
useMxCheck($mx)
getMxCheck($mx)
useDeepMxCheck($deep)
getDeepMxCheck()
isMxSupported()

https://tools.ietf.org/html/rfc2822
An MX record is a type of record used in the Domain Name System (DNS). MX records define one or several mail server addresses assigned

to recipients domain.

276

Checking Input Data with Validators

Table 9.2. Public methods of the EmailAddress validator

Method name

Description

getMXRecord()

After validation, returns the found MX record


information.

As you can see from table, the EmailAddress validator, additionally to the isValid() and
getMessages() methods, provides the constructor method to which you can (optionally) pass
the complete list of options for initializing the validator.
All standard validators have the constructor method (optionally) accepting an array of
options for configuring the validator when instantiating it manually.

The EmailAddress class also provides a number of methods that can be used for setting specific
validator options.
The useDomainCheck() method tells whether to check the host name for correctness, or not. By
default, this check is enabled. The setAllow() method provides an ability to specify which types
of host names are allowed. You can pass an OR combination of the ALLOW_-prefixed constants
to the setAllow() method:

ALLOW_DNS Allow a domain name (this is the default),


IP_ADDRESS Allow an IP address,
ALLOW_LOCAL Allow local network name,
ALLOW_ALL Allow all of the above.

Internally, the EmailAddress validator uses the Hostname validator for checking the
host name part of an E-mail address. Optionally, you can attach a custom host name
validator by using the setHostnameValidator() method, however it is unlikely you
will need to do such.

The useMxCheck() method tells whether the validator should connect to the recipients host and
query the DNS server for the MX record(s). If the server has no MX records, than the validation
fails. You can additionally use the useDeepMxCheck() method to tell the validator to compare the
mail server addresses extracted from the MX records against the black list of reserved domain
names, and perform additional checks per each detected address.
It is not recommended to perform MX check (and deep MX check), because that may
take a lot of time and increase the web page load time. By default, these checks are
disabled.

Below, we provide code examples showing two equivalent methods of manual creating of an
instance of the EmailAddress validator, setting its options and checking an input value:
Example 1. Passing options to the constructor method.
The ALLOW_-prefixed constants are provided by the Hostname validator.

Checking Input Data with Validators


1
2
3
4

277

<?php
// Optionally, define a short alias for the validator class name.
use Zend\Validator\EmailAddress;
use Zend\Validator\Hostname;

5
6
7
8
9
10
11

// Create an instance of the validator, passing options to the constructor.


$validator = new EmailAddress(array(
'allow' => Hostname::ALLOW_DNS|Hostname::ALLOW_IP|Hostname::ALLOW_LOCAL,
'mxCheck' => true,
'deepMxCheck' => true
));

12
13
14
15

// Validate an E-mail address.


$isValid = $validator->isValid('name@example.com'); // Returns true.
$isValid2 = $validator->isValid('abc'); // Returns false.

16
17
18
19
20

if(!$isValid2) {
// Get error messages in case of validation failure.
$errors = $validator->getMessages();
}

In the code above, we create the EmailAddres validator object with the help of the new operator
(line 7). We pass the array of options to the constructor. We use the allow key to allow an E-mail
address to be a domain name, an IP address or local network address. Also, we use the mxCheck
and deepMxCheck to enable MX record check and deep MX record check, respectively.
In line 14, we call the isValid() method and pass it the string value name@example.com to
be checked. The expected output of this call is the boolean true.
In line 15, we pass the abc string value to the validator. The validation procedure is expected to
fail (false is returned). Then, the error messages are retrieved with the getMessages() method
(line 19).
Example 2. Without passing options to the constructor.
1
2
3
4

<?php
// Optionally, define a short alias for the validator class name.
use Zend\Validator\EmailAddress;
use Zend\Validator\Hostname;

5
6
7

// Create an instance of the validator.


$validator = new EmailAddress();

8
9
10
11
12

// Optionally, configure the validator


$validator->setAllow(
Hostname::ALLOW_DNS|Hostname::ALLOW_IP|Hostname::ALLOW_LOCAL);
$validator->useMxCheck(true);

Checking Input Data with Validators


13

278

$validator->useDeepMxCheck(true);

14
15
16
17

// Validate an E-mail address.


$isValid = $validator->isValid('name@example.com'); // Returns true.
$isValid2 = $validator->isValid('abc'); // Returns false.

18
19
20
21
22

if(!$isValid2) {
// Get error messages in case of validation failure.
$errors = $validator->getMessages();
}

In the code above, we create the EmailAddres validator object with the help of the new operator
(line 7).
In lines 10-13, we configure the validator. We call the setAllow() method to allow an Email address to be a domain name, an IP address or local network address. Also, we use the
useMxCheck() and useDeepMxCheck() to enable MX record check and deep MX record check,
respectively.
In line 16, we call the isValid() method and pass it the string value name@example.com to
be checked. The expected output of this call is the boolean true.
In line 17, we pass the abc string value to the validator. The validation procedure is expected
to fail. Then, the error messages are retrieved with the getMessages() method (line 21).

9.4.2 Method 2. Using StaticValidator Wrapper


An alternative way of manual validator instantiation is by using the StaticValidator class. The
StaticValidator class is some kind of a proxy designed for automatic validator instantiation,
configuration and execution. For example, lets consider how to create the same EmailAddress
validator, configure it and call its isValid() method:
1
2
3
4
5
6
7
8
9
10
11
12

<?php
// Create and execute the EmailAddress validator through StaticValidator proxy.
$validatedValue = \Zend\Filter\StaticValidator::execute('name@example.com',
'EmailAddress',
array(
'allow' =>
Hostname::ALLOW_DNS|
Hostname::ALLOW_IP|
Hostname::ALLOW_LOCAL,
'mxCheck' => true,
'deepMxCheck' => true
));

13
14

// The expected output is boolean true.

Checking Input Data with Validators

279

The StaticValidator class provides the execute() static method which takes three arguments:
the input value, the name of the filter to apply, and the array of filter-specific options.
In line 3, we call the execute() method to automatically create the EmailAddress validator, call
its setAllowDns(), useMxCheck() and useDeepMxCheck() methods, and pass the input value to
its isValid() method. This is very useful, because can be accomplished in a single call.
The StaticValidator doesnt provide an ability to extract the list of human-readable
validation errors. However, since the StaticValidator is designed to be used outside
forms, and not intended for displaying results to a human, this seems to be not a big
disadvantage.

9.4.3 Method 3. Using an Array Configuration


When using validators with forms validation rules, you typically do not construct a validator
object explicitly as we did in the previous section, instead you pass an array configuration to the
factory class which automatically constructs the validator for you and (optionally) configures it.
We already saw how this works when adding validation rules for the feedback form in Chapter
7.
For example, lets show how to construct the same EmailAddress filter with the help of the
factory:
1
2
3

<?php
// It is assumed that you call the following code inside of the form model's
// addInputFilter() method.

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

$inputFilter->add(array(
// ...
'validators' => array(
array(
'name' => 'EmailAddress',
'options' => array(
'allow' => \Zend\Validator\Hostname::ALLOW_DNS,
'useMxCheck' => false,
'useDeepMxCheck' => false,
),
),
),
// ...
);

In the code above, we call the add() method provided by the InputFilter container class (line
5). The add() method takes an array which has the validators key. You typically register the
validators under that key (line 7). Validators registered under that key are inserted into validator
chain in the order they appear in the list.

Checking Input Data with Validators

280

A validator configuration typically consists of the name (line 9) and options (line 10). The name
is a fully qualified validator class name (e.g. \Zend\Validator\EmailAddress) or its short alias
(EmailAddress). The options is an array consisting of validator-specific options. When the
factory class instantiates the validator, it passes the list of options to the validators constructor,
and the constructor initializes the validator as needed.

9.5 About Validator Plugin Manager


When creating a validator with a factory, you can use either the fully qualified validator
class name or its short alias. The short aliases for the standard validators are defined by the
ValidatorPluginManager class.
The ValidatorPluginManager class defines validator aliases.

A standard validators alias is typically the same as class name. For example, the class
Zend\Validator\EmailAddress has the short alias EmailAddress.
The validator plugin manager is internally used by the InputFilter container class for instantiating the standard validators.

9.6 Validator Usage Examples


Next, we will consider the usage of the most important standard validators. These describe the
methods (and options) a validator has, and provide a code example showing how to instantiate
and apply the validator to input data.

9.6.1 Validators for Checking Value Conformance to Certain


Format
In this section, we will consider usage examples of the validators from the group of validators
designed for checking if input value conforms to certain format.
9.6.1.1 Ip Validator
The Ip validator class is designed to check if the input value is a valid IP address. If the input value
is an IPv4 address, IPv6 address, IPvFuture address, or IPv6 literal address, the validator
returns boolean true; otherwise it returns false. On failure, error messages can be extracted
with the validators getMessages() method.
Public methods provided by the Ip validator are listed in table 9.3:
An Internet Protocol version 4 (IPv4) address typically consists of four octets of the address expressed separated by periods, like
192.168.56.101.
An Internet Protocol version 6 (IPv6) address typically consists of eight groups of four hexadecimal digits separated by colons, such as
2001:0db8:85a3:0000:0000:8a2e:0370:7334.
IPvFuture is loosely defined in the Section 3.2.2 of RFC 3986.
A literal IPv6 address is a modification of a usual IPv6 address for using inside of a URL. (The problem with original IPv6 addresses is that
the : and . characters are delimiters in URLs.)

281

Checking Input Data with Validators

Table 9.3. Public methods of the Ip validator

Method name

Description

__construct($options)
isValid($value)
getMessages()
setOptions($options)

Constructs the validator. Accepts the list of options.


Returns true if and only if value is a valid IP address.
If validation failed, this method will return an array of error messages.
Sets validator options.

The setOptions() method provides an ability to set allowed types of IP addresses:

allowipv4 to allow IPv4 addresses;


allowipv6 to allow IPv6 addresses;
allowipvfuture to allow IPvFuture addresses;
allowliteral to allow IPv6 literal addresses.

By default all the above are allowed, except the IPv6 literal address.
Below, a code example demonstrating the usage of the Ip validator is provided.
<?php
use Zend\Validator\Ip;
// Create Ip validator.
$validator = new Ip();
// Configure the validator.
$validator->setOptions(array(
'allowipv4'
=> true,
'allowipv6'
=> true,
'allowipvfuture' => false,
'allowliteral'
=> true,
);

//
//
//
//

Allow
Allow
Allow
Allow

IPv4 addresses.
IPv6 addresses.
IPvFuture addresses.
IP addresses in literal format.

// Check if input value is a valid IP address (IPv4).


$isValid = $validator->validate('192.168.56.101'); // Returns true
// Check if input value is a valid IP address (IPv6).
$isValid2 = $validator->validate(
'2001:0db8:85a3:0000:0000:8a2e:0370:7334'); // Returns true
// Pass an invalid string (not containing an IP address).
$isValid3 = $validator->validate('abc'); // Returns false

9.6.1.2 Hostname Validator


The Hostname validator is designed to check if a given value is a host name belonging to set of
allowed host name types. The types are:

282

Checking Input Data with Validators

a DNS Hostname (e.g. example.com);


an IP address (e.g. 192.168.56.101);
a local host name (e.g. localhost).
The public methods provided by the validator are listed in table 9.4:
Table 9.4. Public methods of the Hostname validator

Method name

Description

__construct($options)
isValid($value)

Constructs the validator. Accepts the list of options.


Returns true when the value is a valid host name; otherwise
returns false.
If validation failed, this method will return an array of error
messages.
Optionally, allows to set own IP address validator.
Retrieves attached IP address validator.
Defines the type(s) of host names which are allowed.
Returns allowed host names types.
Defines if Internationalized Domain Names (IDN) check is
enabled. This option defaults to true.
Returns true if IDN check is enabled.
Defines if Top Level Domain (TLD) check is enabled. This option
defaults to true.
Returns true if TLD check is enabled.

getMessages()
setIpValidator($ipValidator)
getIpValidator()
setAllow()
getAllow()
useIdnCheck()
getIdnCheck()
useTldCheck()
getTldCheck()

You can set which host name types are allowed with the setAllow() method. It accepts a
combination of the following constants:

ALLOW_DNS Allows Internet domain names (e.g., example.com);


ALLOW_IP Allows IP addresses;
ALLOW_LOCAL Allows local network names (e.g., localhost, www.localdomain);
ALLOW_URI Allows URI host names.
ALLOW_ALL Allows all types of host names.

By default, only Internet domain names are allowed.


The host name check consists of several stages, some of which may be omitted based on validator
options:
1. If the input value looks like an IP address, it is checked with the internal IP address validator. You can override which IP address validator to use for this by the setIpValidator()
method.
2. The host name is separated into domain parts (separated with dot . character).
3. The top-level domain is checked against the white list of available TLDs. (You can disable
this check with the useTldCheck() method.)

283

Checking Input Data with Validators

4. Each domain part is checked based on the rules for acceptable domain names. If a domain
name is an IDN , it is checked against the rules for valid IDNs. (You can disable IDN check
with useIdnCheck() method.)
Below, a code example demonstrating the usage of the Hostname validator is provided.
<?php
use Zend\Validator\Hostname;
// Create the Hostname validator.
$validator = new Hostname();
// Configure the validator.
$validator->setAllow(Hostname::ALLOW_DNS|Hostname::ALLOW_IP);
// Check a host name.
$isValid = $validator->validate('site1.example.com'); // Returns true.
$isValid2 = $validator->validate('abc'); // Returns false (not a valid host na\
me).

9.6.1.3 Uri Validator


The Uri validator is designed to check whether the input value is a Uniform Resource Identifier
(URI) . On failure, error messages can be extracted with the validators getMessages() method.
Dont be confused with the term URI. In most cases, you may think of URI as of a usual
URL.

The public methods provided by the Uri validator are listed in table 9.5:
Table 9.5. Public methods of the Uri validator

Method name

Description

__construct($options)
isValid($value)

Constructs the validator. Accepts the list of options.


Returns true when the value is a valid URI; otherwise
returns false.
If validation failed, this method will return an array of error
messages.
Sets the URI handler object for this validator.
Retrieves the URI handler object.
Tells the validator whether absolute URIs are accepted.
Returns true if absolute URIs are accepted.
Tells the validator whether relative URIs are accepted.
Returns true if relative URIs are accepted.

getMessages()
setUriHandler($uriHandler)
getUriHandler()
setAllowAbsolute($allowAbsolute)
getAllowAbsolute()
setAllowRelative($allowRelative)
getAllowRelative()

An internationalized domain name (IDN) is an Internet domain name that contains at least one label that is displayed, in whole or in part,
in a language-specific script or alphabet, like Arabic, Chineese or Russian.
A Uniform Resource Identifier (URI) is a compact sequence of characters that identifies an abstract or physical resource. An Uniform
Resource Locator (URL) is a type of URI. But, that doesnt mean all URIs are URLs.

Checking Input Data with Validators

284

Internally, the Uri validator uses so called URI handler object, which is responsible for parsing an
URI string. By default, Zend\Uri\Uri class is used as the URI handler. (You can set your custom
URI handler with the setUriHandler() method, if you wish.)
An URI can be absolute or relative. For example, an absolute URI is http://example.com/blog/2014/02/02/edit,
while a relative URI is 2014/02/02/edit. You can specify whether the validator should consider
absolute and/or relative URIs acceptable. For that, you use the setAllowAbsolute() and
setAllowRelative() methods, respectively. By default, both are treated as acceptable URI types.
Below, a code example demonstrating the usage of the Uri validator is provided.
<?php
use Zend\Validator\Uri;
// Create the Uri validator.
$validator = new Uri();
// Configure the validator.
$validator->setAllowAbsolute(true);
$validator->setAllowRelative(true);
// Check an URI.
$isValid = $validator->validate(
'http://site1.example.com/application/index/index'); // Returns t\
rue.
$isValid2 = $validator->validate('index/index'); // Returns true.

9.6.1.4 Date Validator


The Date validator is intended for checking whether the input data is a date in a given format.
On failure, error messages can be extracted with the validators getMessages() method.
Public methods provided by the Date validator are listed in table 9.6:
Table 9.6. Public methods of the Date validator

Method name

Description

__construct($options)
isValid($value)

Constructs the validator. Accepts the list of options.


Returns true when the value is a string containing a date in expected
format; otherwise returns false.
If validation failed, this method will return an array of error messages.
Sets an acceptable date format.
Retrieves the expected format.

getMessages()
setFormat($format)
getFormat()

To set the expected date format, you can use the setFormat() method.

Checking Input Data with Validators

285

Internally, the DateTimeFormatter filter uses the DateTime class from the PHP standard
library for converting and formatting dates. For available date formats, please refer to
the PHP documentation for the DateTime class.

Below, a code example demonstrating the usage of the Date validator is provided.
<?php
use Zend\Validator\Date;
// Create validator instance.
$validator = new Date();
// Configure validator.
$validator->setFormat('Y-m-d');
// Check if the input value is a date having expected format.
$isValid = $validator->isValid('2014-04-04'); // returns true.
$isValid2 = $validator->isValid('April 04, 2014'); // returns false (format is\
unexpected).

9.6.1.5 Regex Validator


This validator allows you to validate if a given string conforms some regular expression. It returns
true if the string matches the regular expression, otherwise it returns false. On failure, error
messages can be extracted with the validators getMessages() method.
The public methods provided by the Regex validator are listed in table 9.7:
Table 9.7. Public methods of the Regex validator

Method name

Description

__construct($options)
isValid($value)

Constructs the validator. Accepts the list of options.


Returns true if and only if $value matches the given regular expression
pattern.
If validation failed, this method will return an array of error messages.
Sets the regular expression pattern.
Retrieves the regular expression pattern.

getMessages()
setPattern($pattern)
getPattern()

The setPattern() method allows to set the regular expression to match against.
For regular expressions syntax and examples, it is recommended that your refer to the
PCRE Patterns section of the PHP documentation.

Below, a code example demonstrating the usage of the Regex validator is provided. It uses the
regular expression to check if the input string is a valid IPv4 address (such address typically
consists of four groups of digits separated with dots).

Checking Input Data with Validators

286

<?php
use Zend\Validator\Regex;
// Create Regex validator.
$validator = new Regex();
// Set regular expression to check for an IP address.
$validator->setPattern('\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b');
// Check for regular expression match.
$isValid = $validator->isValid("127.0.0.1"); // returns true.
$isValid2 = $validator->isValid("123"); // returns false.

9.6.2 Validators for Checking a Numerical Value Lies in a Given


Range
In this section, we will consider usage examples of the validators from the group of validators
designed for checking if input value lies in a given range.
9.6.2.1 NotEmpty Validator
The NotEmpty validator allows to check if input value is not empty. This is often useful when
working with form elements or other user input, where you can use it to ensure required elements
have values associated with them.
The public methods provided by the NotEmpty validator are listed in table 9.8:
Table 9.8. Public methods of the NotEmpty validator

Method name

Description

__construct($options)
isValid($value)
getMessages()
setType($type)
getType()
getDefaultType()

Constructs the validator. Accepts the list of options.


Returns true if and only if $value is not an empty value.
If validation failed, this method will return an array of error messages.
Set the value types that to consider as empty values.
Returns the types.
Returns the default types.

The setType() method specifies which variable types to consider as an empty value. This method
accepts the single argument $type which can be either an OR combination of the constants listed
in table 9.9, or an array containing the literal equivalents of those constants.

287

Checking Input Data with Validators

Table 9.9. Type constants

Constant

Numeric Value

Literal Equivalent

Description

BOOLEAN

boolean

INTEGER
FLOAT
STRING

2
4
8

integer
float
string

ZERO

16

zero

EMPTY_ARRAY

32

array

NULL
PHP

64
127

null
php

SPACE

128

space

OBJECT

256

object

OBJECT_STRING

512

objectstring

OBJECT_COUNT

1024

objectcount

ALL

2047

all

Consider boolean false as an empty


value.
Consider integer 0 as an empty value.
Consider float 0.0 as an empty value.
Consider empty string as an empty
value.
Consider string containing the single
character zero (0) as an empty value.
Consider an empty array as an empty
value.
Consider null as an empty value.
Consider the value empty if the empty()
PHP function would return true on it.
Consider a string which contains only
white spaces as an empty value.
Returns true. false will be returned
when object is not allowed but an object
is given.
Returns false when an object is given
and its __toString() method returns an
empty string.
Returns false when an object is given, it
has an Countable interface and its count
is 0.
Consider all above types as empty values.

Below, a code example demonstrating the usage of the NotEmpty validator is provided.
<?php
use Zend\Validator\NotEmpty;
// Create validator instance.
$validator = new NotEmpty();
// Configure validator.
$validator->setType(NotEmpty::ALL);
// Check if
$isValid1 =
$isValid2 =
$isValid3 =

input value not empty.


$validator->isValid('some string'); // returns true
$validator->isValid(''); // returns false
$validator->isValid(0); // returns false

9.6.2.2 Between Validator


The Between validator checks whether a number lies in a certain range (min, max), either
inclusively (by default) or exclusively.

Checking Input Data with Validators

288

The public methods provided by the Between validator are listed in table 9.10:
Table 9.10. Public methods of the Between validator

Method name

Description

__construct($options)
isValid($value)
getMessages()
setMin($min)
getMin()
setMax($max)
getMax()
setInclusive($inclusive)

Constructs the validator. Accepts the list of options.


Returns true if and only if values length is within the given range.
If validation failed, this method will return an array of error messages.
Sets the minimum limit.
Retrieves the minimum limit.
Sets the maximum limit.
Retrieves the maximum limit.
Sets whether to compare if the value lies in the given boundaries
inclusively.
Returns the inclusive option.

getInclusive()

The range can be set with the setMin() and setMax() methods.
By default, the validator performs inclusive comparisons (to check if the value belongs to the
given range, it compares if the value is greater or equal to its lower bound, and if the value is
lower or equal to its upper bound). You can change this with the setInclusive() method. It tells
the validator whether to perform inclusive comparisons (pass true as the argument) or exclusive
comparisons (pass false as the argument).
Below, a code example demonstrating the usage of the Between validator is provided.
<?php
use Zend\Validator\Between;
// Create validator instance.
$validator = new Between();
// Configure validator.
$validator->setMin(1);
$validator->setMax(10);
$validator->setInclusive(true);
$isValid1
$isValid2
$isValid3
$isValid4

=
=
=
=

$validator->isValid(5); // returns true.


$validator->isValid(10); // returns true.
$validator->isValid(0); // returns false (value is too small).
$validator->isValid(15); // returns false (value is too big).

9.6.2.3 InArray Validator


The InArray validator checks whether the input value belongs to the given array of values. The
public methods provided by the InArray validator are listed in table 9.11:

Checking Input Data with Validators

289

Table 9.11. Public methods of the InArray validator

Method name

Description

__construct($options)
isValid($value)
getMessages()
setHaystack($haystack)
getHaystack()
setStrict($strict)
getStrict()
setRecursive($recursive)
getRecursive()

Constructs the validator. Accepts the list of options.


Returns true if and only if value belongs to the given array.
If validation failed, this method will return an array of error messages.
Sets the array to search in.
Returns the array of allowed values.
Sets strict comparison mode.
Whether strict comparison mode enabled?
Tells the validator to search recursively.
Whether the recursive search is enabled?

The setHaystack() method allows to set the array of allowed values. The isValid() method
will search through this array for the presence of the input $value.
If the array contains nested values and you want to search among them recursively, then use
setRecursive() method. This method takes the single boolean flag. If the flag is true, than the
search will be performed recursively; otherwise the nested levels will be ignored.
The setStrict() method provides an ability to tell the validator how to compare the input value
and the values in array. This may be a combination of the following constants:
COMPARE_NOT_STRICT Do not perform strict check of variable type.
COMPARE_NOT_STRICT_AND_PREVENT_STR_TO_INT_VULNERABILITY Do not perform strict check
of variable type, but prevent false positive comparisons of string to integer (e.g. "asdf" ==
0). This is the default option.
COMPARE_STRICT Check both variable type and value.
Below, a code example demonstrating the usage of the InArray validator is provided.
<?php
use Zend\Validator\InArray;
// Create validator instance.
$validator = new InArray();
// Configure validator.
$validator->setHaystack(array(1, 3, 5));
// Perform validation.
$isValid1 = $validator->isValid(1); // returns true.
$isValid2 = $validator->isValid(2); // returns false.

Checking Input Data with Validators

290

9.6.2.4 StringLength Validator


The StringLength validator checks whether the input string length belongs to the given range,
inclusively. It returns true if and only if the string length of value is at least the min option and
no greater than the max option (when the max option is not null).
The public methods provided by the StringLength validator are listed in table 9.12:
Table 9.12. Public methods of the StringLength validator

Method name

Description

__construct($options)
isValid($value)
getMessages()
setMin($min)
getMin()
setMax($max)
getMax()
setEncoding($encoding)
getEncoding()

Constructs the validator. Accepts the list of options.


Returns true if and only if values length is within the given range.
If validation failed, this method will return an array of error messages.
Sets the minimum limit.
Retrieves the minimum limit.
Sets the maximum limit.
Retrieves the maximum limit.
Sets a new encoding to use.
Retrieves the encoding.

By default, the StringLength validator considers any string length as valid. Use the setMin()
and/or setMax() methods to set lower and upper limits for the allowable string length. There are
three possible ways you can do that:
Use only the setMin() method to allow strings with a lower-bound minimum length, but
unbound upper length;
Use only the setMax() method to allow strings with zero minimum length and an upperbound maximum length;
Use both the setMin() and setMax() methods to allow strings with a length laying
between the lower and upper bounds.
By default, the PHP engine uses the UTF-8 encoding for strings. If your input string uses a
different encoding, you should specify it encoding with the setEncoding() validators method.
Below, a code example demonstrating the usage of the StringLength validator is provided.
<?php
use Zend\Validator\StringLength;
// Create validator instance.
$validator = new StringLength();
// Configure the validator.
$validator->setMin(1);
$validator->setMax(10);

291

Checking Input Data with Validators

$isValid1 = $validator->isValid("string"); // returns true.


$isValid2 = $validator->isValid(""); // returns false (value is too short).
$isValid3 = $validator->isValid(
"a very long string"); // returns false (value is too long).

9.6.3 Organizing Validators in a Chain


Validators can be organized in a sequence. This is accomplished by the ValidatorChain class.
When such a compound validator is run, the input value is passed to all validators in turn. The
ValidatorChain validators isValid() method returns true if all validators in the chain return
true; otherwise it returns false.
The ValidatorChain class is internally used by the InputFilter container class for
storing the sequence of validators attached to a form models field.

Public methods provided by the ValidatorChain class are presented in table 9.13:
Table 9.13. Public methods of the ValidatorChain validator

Method name

Description

isValid($value)

Returns true if all validators in the chain return


true.
Returns the array of validation error messages.
Returns the array of validators in the chain.
Returns count of validators in the chain.
Attaches a validator to the end of the chain.
Adds a validator to the beginning of the chain.

getMessages()
getValidators()
count()
attach($validator, $breakChainOnFailure)
prependValidator($validator,
$breakChainOnFailure)
attachByName($name, $options,
$breakChainOnFailure)
prependByName($name, $options,
$breakChainOnFailure)
merge($validatorChain)

Use the plugin manager to add a validator by


name.
Use the plugin manager to prepend a validator by
name.
Merge the validator chain with the one given in
parameter.

An example validator chain is shown in figure 9.2. It consists of the NotEmpty validator followed
by the StringLength validator, which in turn is followed by the Date validator. When this chain
is executed, first, the NotEmpty validator is run checking that the value is a non-empty value,
then the StringLength validator is run checking that the length of the input string belongs to
range (1, 16), inclusively, and finally, the Date validator is run checking that the input value is a
date in format YYYY-MM-DD.

Figure 9.2. Validator chain

To construct the filter chain like in figure 9.2, we can use the following code:

292

Checking Input Data with Validators

<?php
// Instantiate the validator chain.
$validator = new \Zend\Validator\ValidatorChain();
// Insert validators into validator chain.
$validator->attachByName('NotEmpty');
$validator->attachByName('StringLength', array('min'=>1, 'max'=>16));
$validator->attachByName('Date', array('format'=>'Y-m-d'));
// Execute all validators in the chain.
$isValid = $validator->isValid('2014-04-04'); // Returns true.

9.6.4 Custom Validation with the Callback Validator


The Callback validator can be a wrapper for your custom validation algorithm. For example,
this may be useful when a standard validator is not suitable, and you need to apply your own
checking algorithm to the data. The public methods provided by the Callback validator are listed
in table 9.14.
Table 9.14. Public methods of the Callback validator

Class name

Description

isValid($value, $context)
getMessages()

Executes a callback function as a validator.


If validation failed, this method will return an array of error
messages.
Sets a new callback for this filter.
Returns callback set for the filter.
Sets options for the callback.
Get parameters for the callback.

setCallback($callback)
getCallback()
setCallbackOptions($options)
getCallbackOptions()

As you can see from the table, the Callback validator provides the setCallback() and
setCallbackOptions() methods that can be used to set the callback function or class method
and (optionally) pass it one or several parameters.
9.6.4.1 Example
To demonstrate the usage of the Callback validator, lets add the phone number validator to our
ContactForm form model class. The validator would check a telephone number entered by site
visitor.
The validator needs to be able to check for two common phone number formats:
international format looking like 1 (234) 567-8901;
and local format, which looks like 567-8901.
Because ZF2 does not provide a standard validator for accomplishing such phone filtering
operation, we will use the Callback wrapper validaotr. To do that, we will make the following
changes to the code of our ContactForm class:

Checking Input Data with Validators


1
2
3
4
5
6
7

<?php
// ...
class ContactForm extends Form
{
// ..
protected function addElements() {
// ...

// Add "phone" field


$this->add(array(
'type' => 'text',
'name' => 'phone',
'attributes' => array(
'id' => 'phone'
),
'options' => array(
'label' => 'Your Phone',
),
));

9
10
11
12
13
14
15
16
17
18
19
20

21
22

private function addInputFilter() {

23

// ...

24
25

$inputFilter->add(array(
'name'
=> 'phone',
'required' => true,
'validators' => array(
array(
'name' => 'Callback',
'options' => array(
'callback' => array($this, 'validatePhone'),
'callbackOptions' => array(
'format' => 'intl'
)
)
)
)
);

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

42
43
44

// Custom validator for a phone number.


public function validatePhone($value, $format) {

45
46

// Determine the correct length and pattern of the phone number,

293

Checking Input Data with Validators

294

// depending on the format.


if($format == 'intl') {
$correctLength = 16;
$pattern = '/^\d\ (\d{3}\) \d{3}-\d{4}$/';
} else { // 'local'
$correctLength = 8;
$pattern = '/^\d{3}-\d{4}$/';
}

47
48
49
50
51
52
53
54
55

// Check phone number length.


if(strlen($value)!=$correctLength)
return false;

56
57
58
59

// Check if the value matches the pattern.


$matchCount = preg_match($pattern, $value);

60
61
62

return ($matchCount!=0)?true:false;

63

64
65

In the code above, we create the phone field in our ContactForm (if you already have such field,
just ignore this).
In line 40, we add the PhoneValidator validator to the input filters validator chain for the
phone field.
In lines 45-58, we have the validatePhone() callback method. The method accepts two
arguments: the $value parameter is the phone number to validate, and the $format parameter
is the expected format of the phone number (intl or local).
Inside of the callback method, we do the following:
1. Calculate the correct length of the phone, check whether the length is correct for the
selected phone number format.
2. Match the phone number against the regular expression pattern for the selected phone
format.

9.7 Writing Own Validator


An alternative of using the Callback validator is writing your own validator class implementing
the ValidatorInterface interface. Then, this validator may be used in forms of your web
application.
To demonstrate how to create your own validator, we will write the PhoneValidator class
encapsulating the phone validation algorithm we used with the Callback validator example.
As you might remember, the base concrete class for all standard validators is the
AbstractValidator class. By analogy, we will also derive our custom PhoneValidator
validator from that base class.

Checking Input Data with Validators

295

We plan to have the following methods in our PhoneValidator validator class (see table 9.18):
Table 9.18. Public methods of the Callback validator

Method name

Description

__construct($options)

Constructor. Accepts an optional argument $options which is needed to


set validator options at once.
Sets the phone format option.
Returns the phone format option.
Returns true when the value is a valid phone number; otherwise returns
false.
If validation failed, this method will return an array of error messages.

setFormat($format)
getFormat()
isValid($value)
getMessages()

For the PhoneValidator, we will have three possible error messages:


If a non-scalar value is passed to the validator, it will generate the error message The
phone number must be a scalar value;
If the international phone format is selected, and the phone number entered doesnt
match this format, the validator will generate the message The phone number must be in
international format;
If the local phone format is selected and the phone number entered doesnt match the
format, the validator will generate the message The phone number must be in local
format.
To start, create the PhoneValidator.php file in the Application/Service directory under the
modules source directory . Put the following code into that file:
1
2

<?php
namespace Application\Service;

3
4

use Zend\Validator\AbstractValidator;

5
6
7
8

// This validator class is designed for checking a phone number for


// conformance to the local or to the international format.
class PhoneValidator extends AbstractValidator {

9
10
11
12

// Phone format constants.


const PHONE_FORMAT_LOCAL = 'local'; // Local phone format.
const PHONE_FORMAT_INTL = 'intl'; // International phone format.

13
14
15
16
17

// Available validator options.


protected $options = array(
'format' => self::PHONE_FORMAT_INTL
);
The PhoneValidator class may be considered as a service model, because its goal is to process data, not to store it.

Checking Input Data with Validators


18
19
20
21
22

// Validation failure message IDs.


const NOT_SCALAR = 'notScalar';
const INVALID_FORMAT_INTL = 'invalidFormatIntl';
const INVALID_FORMAT_LOCAL = 'invalidFormatLocal';

23
24
25
26
27
28
29
30

// Validation failure messages.


protected $messageTemplates = array(
self::NOT_SCALAR => "The phone number must be a scalar value",
self::INVALID_FORMAT_INTL
=> "The phone number must be in international format",
self::INVALID_FORMAT_LOCAL => "The phone number must be in local format",
);

31
32
33

// Constructor.
public function __construct($options = null) {

34

// Set filter options (if provided).


if(is_array($options)) {

35
36
37

if(isset($options['format']))
$this->setFormat($options['format']);
}

38
39
40
41

// Call the parent class constructor.


parent::__construct($options);

42
43
44

45
46
47

// Sets phone format.


public function setFormat($format) {

48

// Check input argument.


if($format!=self::PHONE_FORMAT_LOCAL &&
$format!=self::PHONE_FORMAT_INTL) {
throw new \Exception('Invalid format argument passed.');
}

49
50
51
52
53
54

$this->options['format'] = $format;

55
56

57
58
59

// Validates a phone number.


public function isValid($value) {

60
61
62
63

if(!is_scalar($value)) {
$this->error(self::NOT_SCALAR);
return $false; // Phone number must be a scalar.

296

Checking Input Data with Validators

297

64
65

// Convert the value to string.


$value = (string)$value;

66
67
68

$format = $this->options['format'];

69
70

// Determine the correct length and pattern of the phone number,


// depending on the format.
if($format == self::PHONE_FORMAT_INTL) {
$correctLength = 16;
$pattern = '/^\d \(\d{3}\) \d{3}-\d{4}$/';
} else { // self::PHONE_FORMAT_LOCAL
$correctLength = 8;
$pattern = '/^\d{3}-\d{4}$/';
}

71
72
73
74
75
76
77
78
79
80

// First check phone number length


$isValid = false;
if(strlen($value)==$correctLength) {
// Check if the value matches the pattern.
if(preg_match($pattern, $value))
$isValid = true;
}

81
82
83
84
85
86
87
88

// If there were an error, set error message.


if(!$isValid) {
if($format==self::PHONE_FORMAT_INTL)
$this->error(self::INVALID_FORMAT_INTL);
else
$this->error(self::INVALID_FORMAT_LOCAL);
}

89
90
91
92
93
94
95
96

// Return validation result.


return $isValid;

97
98

99
100

From line 2, you can see that the validator class lives in the Application\Service namespace.
In line 8, we define the PhoneValidator class. We derive our validator class from the AbstractValidator
base class to reuse the functionality it provides. Line 4 contains the short alias for the AbstractValidator
class.
In lines 11-12, for convenience, we define the phone format constants (PHONE_FORMAT_INTL for
international format and PHONE_FORMAT_LOCAL for local format). These are the equivalents of the
intl and local strings, respectively.

Checking Input Data with Validators

298

In lines 15-17, we define the $options private array variable which is an array having the single
key named format. This key will contain the phone format option for our validator.
In lines 20-22, we define the error message identifiers. We have three identifiers (NOT_SCALAR,
INVALID_FORMAT_INTL and INVALID_FORMAT_LOCAL), because our validator may generate three
different error messages. These identifiers are intended for distinguishing different error messages by machine, not by human.
In lines 25-30, we have the $messageTemplates array variable that contains mapping before
error message identifiers and their textual representations. The textual messages are intended
for displaying to a human.
In lines 33-44, we have the constructor method which takes the single argument $options. When
constructing the validator manually, you may omit this parameter. But, when the validator is
constructed by the factory class, the factory will pass validation options to validators constructor
through this argument.
In lines 47-56 and 59-64, we have the setFormat() and getFormat() methods that allow to set
and retrieve the current phone format, respectively.
In lines 59-99, we have the isValid() method. This method encapsulates the phone number
checking algorithm. It takes the $value parameter, performs the regular expression match, and
returns true on success.
On failure, the isValid() method it returns the boolean false, and the list of errors can be
retrieved by the getMessages() method.
You might notice that we didnt define the getMessages() method in our
PhoneValidator class. This is because that we inherited this method from the
AbstractValidator base class. Inside of our isValid() method, for generating error
messages, we also used the error() protected method provided by the base class (lines
62, 92, 94).

9.7.1 Using the PhoneValidator Class


When the PhoneValidator validator class is ready, you can easily start using it in the feedback
form (or in another form) as follows. It is assumed that you call the following code inside of the
ContactForm::addInputFilter() method:
$inputFilter->add(array(
'name'
=> 'phone',
'required' => true,
'validators' => array(
array(
array(
'name' => '\Application\Service\PhoneValidator',
'options' => array(
'format' => PhoneValidator::PHONE_FORMAT_INTL

Checking Input Data with Validators

299

)
),
),
// ...
),
// ...
)
);

You can see how the PhoneValidator validator works in the Form Demo sample application
bundled with this book. Open the http://localhost/contactus page in your web browser. If you
enter some phone number in an incorrect format, the validator will display an error (see figure
9.3).

Figure 9.3. Phone number validation error

If you wish, you can use the PhoneValidator outside of forms, as shown in code example below:
<?php
use Application\Service\PhoneValidator;
// Create PhoneValidator validator
$validator = new PhoneValidator();
// Configure the validator.
$validator->setFormat(PhoneValidator::PHONE_FORMAT_INTL);
// Validate a phone number
$isValid = $validator->isValid('1 (234) 567-8901'); // Returns true.
$isValid2 = $validator->isValid('12345678901'); // Returns false.

Checking Input Data with Validators

300

if(!$isValid2) {
// Get validation errors.
$errors = $validator->getMessages();
}

9.8 Using Filters & Validators Outside a Form


In this section, we will provide an example of how you can use filters and/or validators in your
controller to transform and check the data extracted from GET and/or POST variables.
Lets assume we implement a payment gateway system and need to create a web page displaying
a payment history for the given credit card on given date. This page can be handled by some
paymentHistoryAction() action of a controller class, and the credit card number and date will be
extracted from GET variables. For the paymentHistoryAction() method, we need to implement
some security checks:
we want to ensure that the credit card number looks like a typical credit card number
4532-7103-4122-1359 (conforms to ISO/IEC 7812 standard);
and that the date is in YYYY-MM-DD format.
Below, you can find the code of the action method:
1
2

<?php
namespace Application\Controller;

3
4
5
6
7

use
use
use
use

Zend\Mvc\Controller\AbstractActionController;
Zend\View\Model\ViewModel;
Zend\Filter\StaticFilter;
Zend\Validator\StaticValidator;

8
9

class IndexController extends AbstractActionController {

10
11
12
13

// An action which shows the history of a credit


// card operations on certain date.
public function paymentHistoryAction() {

14
15
16
17

// Get parameters from GET.


$cardNumber = (string)$this->params()->fromQuery('card', '');
$date = (string)$this->params()->fromQuery('date', date("Y-m-d"));

18
19
20
21
22

// Validate credit card number.


$isCardNumberValid = StaticValidator::execute($cardNumber, 'CreditCard');
if(!$isCardNumberValid) {
throw new \Exception('Not a credit card number.');

Checking Input Data with Validators

301

23
24

// Convert date to the right format.


$date = StaticFilter::execute($date, 'DateTimeFormatter',
array('format'=>'Y-m-d'));

25
26
27
28

// The rest of action code goes here...

29
30

return new ViewModel();

31

32
33

Inside the action method, we use the params() controller plugin (lines 16-17) to retrieve two
variables from $_GET super-global array: the card variable (credit card number) and the date
variable (the date).
In line 20, we validate the credit card number with the help of the CreditCard validator. If the
card number is not acceptable, we throw an exception indicating an error (line 22).
In line 26, we use the DateTimeFormatter filter to convert the date to the right format.

9.9 Summary
Validators are designed to take some input data, check it for correctness, and return a boolean
result telling whether the data is correct (and error messages if the data has some errors).
In Zend Framework 2, there are several groups of standard validators:
validators for checking value conformance to certain format;
validators for checking a numerical value lies in a given range;
validators working as proxies to other validators.
In some cases, a standard validator is not suitable, and you need to apply your own checking
algorithm to the input data. In such case, you may use either the Callback validator or write
your own custom validator class.

10 Advanced Usage of Forms


In the Chapter 7, youve learned about form usage basics: what HTML forms are and how you
define form models and form presentation in Zend Framework 2. In this chapter, you will learn
some advanced form usage topics such as security form elements (CAPTCHA and CSRF); file
uploads, and so on.
ZF2 components covered in this chapter:
Component

Description

Zend\Captcha

Implements various CAPTCHA algorithms.

Zend\Form

Contains base form model classes.

Zend\Filter

Contains various filters classes.

Zend\Validator

Implements various validator classes.

Zend\InputFilter

Implements a container for filters/validators.

10.1 Form Security Elements


We will consider the usage of two form security elements provided by Zend Framework 2:
Captcha and Csrf (both classes belong to Zend\Form\Element namespace). By adding those
elements to your form model (and rendering them in a view template), you will make your
form resistant to hacker attacks.

10.1.1 CAPTCHA
A CAPTCHA (stands for Completely Automated Public Turing test to tell Computers and
Humans Apart) is a challenge-response test used in web sites for determining whether the user
is a human or a robot.
There are several types of CAPTCHA. The most widely used one requires that the user type the
letters of a distorted image that is shown on the web page (see figure 10.1 for some examples).

Figure 10.1. CAPTCHA examples

A typical CAPTCHA test works using the following algorithm:

Advanced Usage of Forms

303

1. Some secret sequence of characters (word) is generated server-side.


2. The secret word is saved in a PHP session variable.
3. The distorted image is generated based on the secret word. The image is then displayed on
the web page to site user.
4. The site user is asked to type characters shown on the image.
5. If the characters typed by user are the same as the secret word saved in the session, the
test is considered passed.
The goal of the CAPTCHA test is to protect your form from filling and submission by an
automated process (so called robot). Usually, such robots send spam messages to forums, hack
passwords on site login forms, or perform some other malicious actions.
The CAPTCHA test allows to reliably distinguish humans from robots, because humans
are easily able to recognise and reproduce characters from the distorted image, while
robots are not (at the current stage of evolution of computer vision algorithms).

10.1.1.1 CAPTCHA Types


In Zend Framework 2, there are several CAPTCHA types available (they all belong to the
Zend\Captcha component):
Dumb. This is a very simple CAPTCHA algorithm which requires that site user enter the
word letters in reverse order. We will not consider this type in details here, because it
provides too low protection level.
Image. A CAPTCHA algorithm distorting an image with addition of some noise in form
of dots and line curves (figure 10.1, a).
ReCaptcha. An adapter providing the access to reCAPTCHA service (figure 10.1, c).
The reCAPTCHA is a free service that is provided by Google for generating distorted
images and using them for CAPTCHA test.
Figlet. An unusual CAPTCHA type using FIGlet program instead of an image distortion
algorithm. The FIGlet is an open-source program which generates the CAPTCHA image
of many small ASCII letters (figure 10.1, b).
The Zend\Captcha component provides a unified interface for all CAPTCHA types (the AdapterInterface
interface). The AbstractAdapter base class implements that interface, and all other CAPTCHA
algorithms are derived from the abstract adapter class . The class inheritance diagram is shown
in figure 10.2 below.
As you can see from the figure 10.2, there is another base class for all CAPTCHA types that utilize
some secret word of characters: the AbastractWord class. This base class provides methods for
generating random sequence of characters and for adjusting word generation options.
http://recaptcha.net
The adapter is a design pattern that translates one interface for a class into a compatible interface, which helps two (or several) incompatible

interfaces to work together. Typically, CAPTCHA algorithms have different public methods, but since they all implement AbstractAdapter
interface, the caller may use any CAPTCHA algorithm in the same common manner (by calling the methods provided by the base interface).

304

Advanced Usage of Forms

10.1.1.2 CAPTCHA Form Element & View Helper


ZF2 provides the dedicated form element class and view helper class for letting you use
CAPTCHA fields on your forms.
To add a CAPTCHA field to a form model, you use the Captcha class that belongs to Zend\Form
component and lives in Zend\Form\Element namespace.

Figure 10.2. CAPTCHA adapter classes

The Captcha element class can be used with any CAPTCHA algorithm (listed in the previous
section) from Zend\Captcha component. For this purpose, the element class has the setCaptcha()
method which takes either an instance of a class implementing Zend\Captcha\AdapterInterface
interface, or an array containing CAPTCHA configuration . By the setCaptcha() method, you
can attach the desired CAPTCHA type to the element.
You add the Captcha element to a form model as usual, with the add() method provided by the
Zend\Form\Form base class. As usual, you can pass it either an instance of the Zend\Form\Element\Captcha
class or provide an array of configuration options specific to certain CAPTCHA algorithm (in
that case, the element and its associated CAPTCHA algorithm will automatically be instantiated
and configured by the factory class).
The code example below shows how to use the latter method (passing a configuration array).
We prefer this method because it requires less code to write. It is assumed that you call this code
inside of form models addElements() protected method:

In the latter case (configuration array), the CAPTCHA algorithm will be automatically instantiated and initialized by the factory class

Zend\Captcha\Factory.

Advanced Usage of Forms


1
2
3
4
5
6
7
8
9
10
11
12
13

305

<?php
// Add the CAPTCHA field to the form model
$this->add(array(
'type' => 'captcha',
'name' => 'captcha',
'options' => array(
'label' => 'Human check',
'captcha' => array(
'class' => '<captcha_class_name>', //
// Certain-class-specific options follow here ...
),
),
));

In the example above, we call the add() method provided by the Form base class and pass it an
array describing the element to insert (line 3):
The type key of the array (line 4), as usual, may either be a full name of the element
(Zend\Form\Element\Captcha) or its short alias (captcha).
The name key (line 5) is the value for the name attribute of the HTML form field.
The options key contains the options for the attached CAPTCHA algorithm. The class
key (line 9) may either contain the full CAPTCHA class name (e.g. Zend\Captcha\Image)
or its short alias (e.g. Image). Other, adapter-specific, options may be added to the key
as well. We will show how to do that a little bit later.
For generating the HTML markup for the element, you may use the FormCaptcha view helper
class (belonging to Zend\Form\View\Helper namespace). But, as you might learn from the
previous chapter, typically you use the generic FormElement view helper instead, like shown
in the code below:
<?php echo $this->formElement($form->get('captcha')); ?>

It is assumed that you call the view helper inside of your view template.
Next, we provide three examples illustrating how to use different CAPTCHA types provided
by ZF2: the Image, Figlet and ReCaptcha. We will show how to add a CAPTCHA field to the
feedback form that we used in examples of the previous chapter.

10.1.2 Example 1: Adding Image CAPTCHA to the ContactForm


Image CAPTCHA requires that you have PHP GD extension installed with PNG
support and FT fonts. For information on how to install the extension, please refer
to Appendix A.

To add the Image CAPTCHA to your form model, call the forms add() method as follows:

Advanced Usage of Forms


1
2
3

306

<?php
namespace Application\Form;
// ...

4
5
6
7
8
9

class ContactForm extends Form


{
// ...
protected function addElements() {
// ...

10

// Add the CAPTCHA field


$this->add(array(
'type' => 'captcha',
'name' => 'captcha',
'attributes' => array(

11
12
13
14
15

16

),
'options' => array(
'label' => 'Human check',
'captcha' => array(
'class' => 'Image',
'imgDir' => 'public/img/captcha',
'suffix' => '.png',
'imgUrl' => '/img/captcha/',
'imgAlt' => 'CAPTCHA Image',
'font'
=> './data/font/thorne_shaded.ttf',
'fsize' => 24,
'width' => 350,
'height' => 100,
'expiration' => 600,
'dotNoiseLevel' => 40,
'lineNoiseLevel' => 3
),
),

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

));

35

36
37

Above, the captcha key of the configuration array (see line 20) contains the following parameters
for configuring the Image CAPTCHA algorithm attached to the form element:
the class parameter (line 21) should be either the full CAPTCHA adapter class name
(\Zend\Captcha\Image) or its short alias (Image).
the imgDir parameter (line 22) should be the path to the directory where to save the
generated distorted images (in this example, we will save the images to the APP_DIR/public/img/captcha directory).

Advanced Usage of Forms

307

the suffix parameter (line 23) defines the extension for a generated image file (.png in
this example).
the imgUrl parameter (line 24) defines the base part of the URL for opening generated
CAPTCHA images in a web browser. In this example, site visitors will be able to access
CAPTCHA images using URLs like http://localhost/img/captcha/<ID>, where ID is a
unique ID of certain image.
the imgAlt parameter (line 25) is an (optional) alternative text to show if CAPTCHA image
cant be loaded by the web browser (the alt attribute of <img> tag).
the font parameter (line 26) is the path to the font file. You can download a free TTF
font, for example, from here. In this example, we use Thorne Shaded font, which we
downloaded and put into the APP_DIR/data/font/thorne_shaded.ttf file.
the fsize parameter (line 27) is a positive integer number defining the font size.
the width (line 28) and height parameters (line 29) define the with and height (in pixels)
of the generated image, respectively.
the expiration parameter (line 30) defines the expiration period (in seconds) of the
CAPTCHA images. Once an image expires, it is removed from disk.
the dotNoiseLevel parameter (line 31) and lineNoiseLevel parameter (line 32) define the
image generation options (dot noise level and line noise level, respectively).
To render the CAPTCHA field, add the following lines to your contact-us.phtml view template
file:
<div class="form-group">
<?php echo $this->formLabel($form->get('captcha')); ?>
<?php echo $this->formElement($form->get('captcha')); ?>
<?php echo $this->formElementErrors($form->get('captcha')); ?>
<p class="help-block">Enter the letters above as you see them.</p>
</div>

Finally, create the APP_DIR/public/img/captcha directory that will store generated CAPTCHA
images. Adjust directory permissions to make the directory writable by the Apache Web Server.
In Linux Ubuntu, this is typically accomplished by the following shell commands (replace the
APP_DIR placeholder with the actual directory name of your web application):
mkdir APP_DIR/public/img/captcha
chown -R www-data:www-data APP_DIR
chmod -R 775 APP_DIR

Above, the mkdir command creates the directory, and chmown and chmod commands set the
Apache user to be the owner of the directory and allow the web server to write to the directory,
respectively.
Now, if you open the http://localhost/contactus page in your web browser, the CAPTCHA
image will be generated based on a random sequence of letters and digits saved in session. You
should see something like in the figure 10.3 below.
http://www.1001freefonts.com/

308

Advanced Usage of Forms

When you fill the form fields in and press the Submit button, the letters entered into the Human
check field will be transferred to server as part of HTTP request. Then, on form validation, the
Zend\Form\Element\Captcha class will compare the submitted letters to those stored in PHP
session. If the letters are identical, the form is considered valid; otherwise form validation fails.
Once the PHP renderer processes the view template, it generates HTML markup for the
CAPTCHA element as shown below:
<div class="form-group">
<label for="captcha">Human check</label>
<img width="350" height="100" alt="CAPTCHA Image" src="/img/captcha/df344b37\
500dcbb0c4d32f7351a65574.png">
<input name="captcha[id]" type="hidden" value="df344b37500dcbb0c4d32f7351a65\
574">
<input name="captcha[input]" type="text">
<p class="help-block">Enter the letters above as you see them.</p>
</div>

Figure 10.3. Image CAPTCHA

10.1.3 Example 2: Adding a FIGlet CAPTCHA to the ContactForm


To use the FIGlet CAPTCHA element with your form, replace the form element definition from
the previous example with the following code:

309

Advanced Usage of Forms


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

<?php
// Add the CAPTCHA field
$this->add(array(
'type' => 'captcha',
'name' => 'captcha',
'attributes' => array(
),
'options' => array(
'label' => 'Human check',
'captcha' => array(
'class' => 'Figlet',
'wordLen' => 6,
'expiration' => 600,
),
),
));

Above, the captcha key of the configuration array (see line 9) contains the following parameters
for configuring the Figlet CAPTCHA algorithm attached to the form element:
the class parameter (line 10) should be either the full CAPTCHA adapter class name
(\Zend\Captcha\Figlet) or its short alias (Figlet).
the wordLen parameter (line 11) defines the length of the secret word to be generated.
the expiration parameter (line 12) defines the CAPTCHA expiration period (in seconds).
Now, open the http://localhost/contactus page in your web browser. Once that is done, you
should see a page like in the figure 10.4 below.
Once the PHP renderer processes the view template, it generates HTML markup for the
CAPTCHA element like shown below:
<div class="form-group">
<label for="captcha">Human check</label>
<pre>
__
_
__
__
_
_
___
_
_
| || | || \ \\/ // | \ / ||
/ _ \\ | || | ||
| '--' ||
\ ` // | \/ || | / \ || | || | ||
| .--. ||
| ||
| . . || | \_/ || | \\_/ ||
|_|| |_||
|_||
|_|\/|_||
\___//
\____//
`-` `-`
`-`'
`-` `-`
`---`
`---`

__
__
\ \\/ //
\ ` //
| ||
|_||
`-`'

</pre>
<input name="captcha[id]" type="hidden" value="b68b010eccc22e78969764461be6271\
4">
<input name="captcha[input]" type="text">
<p class="help-block">Enter the letters above as you see them.</p>
</div>

310

Advanced Usage of Forms

Figure 10.4. FIGlet CAPTCHA

10.1.4 Example 3: Adding reCaptcha CAPTCHA to the


ContactForm
It may look surprising, but there are two purposes of the reCAPTCHA service: the main one is to
use wide community of users to help Google to recognize text on images, and the second one is to
generate CAPTCHA images. How does this work? Well, probably youve heard of Google Books
Search that is an automated service for digitizing books. But, sometimes the text recognition
algorithms become stuck on certain word or phrase. In this situation, a human attention would
be required to recognize the word and type it in. People in Google are smart enough to use other
people to do that work for free.
Look at figure 10.1, c for example of reCAPTCHA image. It usually consists of two words. The
first word is known to machine, and the second one is the word on which text recognition
algorithm stucks. The user enters both words, and only the first one is used for CAPTCHA test;
by entering the second one, the user helps digitizing books.
To use reCAPTCHA in your forms, you first need to install the ZendService\Recaptcha
component by typing the following from your command shell:
cd APP_DIR
php composer.phar require zendframework/zendservice-recaptcha *
The ZendService\Recaptcha is not part of ZF2 core distribution, it is so called service component and, because of that, it requires
additional installation.

311

Advanced Usage of Forms

The commands above make your sites directory to be the current working directory and run
Composer dependency manager to install the component.
Next, you have to register on recaptcha.net web-site (figure 10.5) and get the public and private
keys from there.

Figure 10.5. ReCaptcha Registration Page

To add the reCAPTCHA element to your form model, replace the form element definition from
the previous example with the following code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14

// Add the CAPTCHA field


$this->add(array(
'type' => 'captcha',
'name' => 'captcha',
'attributes' => array(
),
'options' => array(
'label' => 'Human check',
'captcha' => array(
'class' => 'ReCaptcha',
// Here, paste your public key string
'privKey' => '<your_public_key>',
// Here, paste your private key string
'pubKey' => '<your_private_key>',
http://www.google.com/recaptcha

312

Advanced Usage of Forms

),
),

15
16
17

));

Above, the captcha key of the configuration array (see line 9) contains the following parameters
for configuring the ReCaptcha CAPTCHA algorithm attached to the form element:
the class parameter (line 10) should be either the full CAPTCHA adapter class name
(\Zend\Captcha\ReCaptcha) or its short alias (ReCaptcha).
the privKey parameter (line 12) should be the public key youve got from recaptcha.net.
the pubKey parameter (line 14) should be the private key youve got from recaptcha.net.

Please note, that in code above, you need to set the privKey parameter with the value
of the public key acquired from recaptcha.net, and the pubKey parameter with the value
of the private key. Exactly in this order; otherwise it will not work.

Now, if you open the http://localhost/contactus page in your web browser, you should see
something like in the figure 10.6 below.

Figure 10.6. reCAPTCHA

10.1.5 CSRF Prevention


Cross-site request forgery (CSRF) is a kind of hacker attack which forces the users browser to
transmit an HTTP-request to an arbitrary site. Through the CSRF attack, the malicious script is

313

Advanced Usage of Forms

able to send unauthorized commands from a user that the web-site trusts. This attack is typically
performed on pages containing forms for submission of some sensitive data (e.g. money transfer
forms, shopping carts etc.)
To better understand how this attack works, take a look at figure 10.7.

Figure 10.7. A CSRF attack example

Figure 10.7 illustrates an example CSRF attack on a payment gateway web-site:


1. You log into your account at payment gateway web site https://payment.com. Please note
that the SSL-protected connection (HTTPS) is used here, but it doesnt protect from such
kind of attacks.
2. Typically, you set check on the Remember Me check box of the login form to avoid
entering user name and password too often. Once you logged in to your account, your
web browser saves your session information to a cookie variable on your machine.
3. On the payment gateway site, you use the payment form https://payment.com/moneytransfer.php
to buy some goods. Please note that this payment form will later be used as a vulnerability
allowing to perform the CSRF attack.
4. Next you use the same web browser to visit some web site you like. Assume the web site
contains cool pictures http://coolpictures.com. Unfortunately, this web site is infected by a
malicious script, masqueraded by an <img src="image.php"> HTML tag. Once you open
the HTML page in your web browser, it loads all its images, thus executing the malicious
image.php script.
5. The malicious script checks the cookie variable, and if it presents, it performs the session
riding and can act on behalf of the logged in user. It is now able to submit the payment
form to the payment gateway site.

Advanced Usage of Forms

314

The above described CSRF attack is possible it the web form on the payment gateway
site does not check the source of the HTTP request. The people who maintain the
payment gateway site must put more attention in making its forms more secure.

To prevent CSRF attacks to a form, one has to require a special token with the form, as folows:
1. For certain form, generate a random sequence of bytes (token) and save it server-side in
PHP session data.
2. Add a hidden field to form and set its value with the token.
3. Once the form is submitted by the user, compare the hidden value passed in the form with
the token saved server-side. If they match, consider the form data secure.
If a malicious user will try to attack the site by submitting the form, he will not be able
to put right token in the form submissions, because the token is not stored in cookies.

10.1.5.1 Example: Adding a CSRF Element to Form


In Zend Framework 2, to add a CSRF protection to your form model, you use the Zend\Form\Element\Csrf
form element class.
The Csrf element has no visual representation (you will not see it on the screen).

To insert a CSRF element to your form model, add the following lines in its addElements()
method:
1
2
3
4
5
6
7
8
9
10

// Add the CSRF field


$this->add(array(
'type' => 'csrf',
'name' => 'csrf',
'options' => array(
'csrf_options' => array(
'timeout' => 600
)
),
));

Above, we use the Forms add() method (line 2), to which we pass a configuration array
describing the CSRF element. The element will be automatically instantiated and initialized by
the factory.
In line 3, we specify the class name for the CSRF element. This either may be the full class name
(Zend\Form\Element\Csrf) or a short alias (csrf).

Advanced Usage of Forms

315

In line 4, we set the name attribute for the element. In this example, we use csrf name, but
you may use any other name, on your choice.
In line 6, inside of csrf_options array, we specify the options specific to Zend\Form\Element\Csrf
class. We set the timeout option to 600 (look at line 7), which means the CSRF check expires in
600 seconds (10 minutes) after form creation.
To render the CSRF field, in your view template .phtml file , add the following line:
<?php echo $this->formElement($form->get('csrf')); ?>

When the PHP renderer evaluates the view template, it generates the HTML markup for the
CSRF field like shown below:
<input type="hidden" name="csrf" value="1bc42bd0da4800fb55d16e81136fe177">

As you can see from the HTML markup code above, the form now contains a hidden
field with a randomly generated token. Since the attacker script doesnt know this
token, it wont be able to submit its correct value, thus the CSRF attack becomes
prevented.

What happens if CSRF element validation fails?


If during the form validation the CSRF check fails, the form is considered invalid and
the user will see it again to fix input errors, but he wont see the error message for the
CSRF element (we dont want hackers to know for sure whats wrong with the form).

10.2 Uploading Files


HTML forms have capability for uploading files of arbitrarily large size . The files are typically
transmitted through HTTP POST method .
By default, HTTP uses the URL encoding for transfers of form data, and you could see how
that encoding looks like when reading the GET and POST Methods section of the previous
chapter. However, this is inefficient for uploading large files, since URL-encoding binary data
dramatically increases the length of the HTTP request. For the purpose of uploading files, it
is instead recommended to use the so called binary transfer encoding described in the next
section.

10.2.1 HTTP Binary Transfer Encoding


A simple HTML form capable of file uploads is shown in the code example below. The binary
encoding type is enabled by setting the enctype attribute of the form with the value of
multipart/form-data:
HTTP file uploads are described in RFC-1867. This mechanism allows to upload large files by using binary content transfer encoding. The
multipart/form-data encoding type is utilized for this purpose.
The HTTP GET method is inefficient for file uploads, because URL length has some upper limit. Also, URL-encoding file data greatly
increases the URL length.

316

Advanced Usage of Forms


1
2
3
4
5

<form action="upload" method="POST" enctype="multipart/form-data">


<input type="file" name="myfile">
<br/>
<input type="submit" name="Submit">
</form>

In line 1, we explicitly set form encoding (enctype attribute) to multipart/form-data to utilize


effective binary content transfer encoding for the form.
In line 2, we define an input field with type file and name myfile. This input field will allow
site visitor to select the file for upload.
If you now save the above mentioned markup to an .html file and open it in your web browser,
you will see the page like in figure 10.7.

Figure 10.7. A simple HTML form capable of file upload

The file element has the Browse button allowing to pick a file for upload. When the site user
picks some file and clicks the Submit button on the form, the web browser will send an HTTP
request to the web server, and the request will contain the data of the file being uploaded. The
example below illustrates how the HTTP request may look like:
1
2
3
4
5
6

POST http://localhost/upload HTTP/1.1


Host: localhost
Content-Length: 488
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64)
Content-Type: multipart/form-data; boundary=----j1bOrwgLvOC3dy7o
Accept-Encoding: gzip,deflate,sdch

7
8
9
10

------j1bOrwgLvOC3dy7o
Content-Disposition: form-data; name="myfile"; filename="Somefile.txt"
Content-Type: text/html

11
12
13
14

(file binary data goes here)


------j1bOrwgLvOC3dy7o
Content-Disposition: form-data; name="Submit"

15
16
17

Submit Request
------j1bOrwgLvOC3dy7o--

Advanced Usage of Forms

317

As you can see from the example above, the HTTP request with multipart/form-data encoding
type looks analogous to a usual HTTP request (has the status line, the headers, and the content
area), however it has the following important differences:
Line 5 sets the Content-Type header with multipart/form-data value; The form is
assembled of the fields marked by the boundary a unique randomly generated sequence
of characters delimiting form fields of each other.
Lines 8-17 represent the content of the HTTP request. The form fields are delimited by the
boundary sequences (lines 8, 13, 17). The data of the file being uploaded are transmitted
in binary format (line 12), and that allows to reduce the content size to its minimum.
By default, PHP engines settings do not allow to upload large files (larger than 2MB).
In order to upload large files, you may need to edit the php.ini configuration file
and modify the post_max_size and upload_max_filesize parameters (please refer to
Appendix A for information on how to do that). Setting these with 100M allows to
upload files up to 100 Mb in size, and this would typically be sufficient. If you plan
to upload very large files up to 1 GB in size, than better set these with 1024M. Do not
forget to restart your Apache Web Server after editing the configuration file.

10.2.2 $_FILES Super-Global Array in PHP


When a site visitor uploads some files to your Apache Web Server, the files are placed to
a temporary location (usually to system temporary directory which is /tmp in Linux and
C:\Windows\Temp in Windows). The PHP script receives the file information to the special superglobal array named $_FILES.
The $_FILES array is analogous to the $_GET and $_POST super-globals. The latter two
are used to store the GET and POST variables, respectively, while the first one is used
to store information about uploaded files.

For example, for the above mentioned simple upload form, the $_FILES super-global array will
look as follows (the output is generated with the var_dump() PHP function):
1
2
3
4
5
6
7
8

array (size=1)
'myfile' =>
array (size=5)
'name' => string 'somefile.txt' (length=12)
'type' => string 'text/plain' (length=10)
'tmp_name' => string 'C:\Windows\Temp\phpDC66.tmp' (length=27)
'error' => int 0
'size' => int 18

As you can see from the example above, the $_FILES array contains an entry per each uploaded
file. For each uploaded file, it contains the following information:

Advanced Usage of Forms

318

name original file name (line 4).


type MIME type of the file (line 5).
tmp_name temporary name for the uploaded file (line 6).
error error code signaling about the status of the upload (line 7); error code zero means

the file was uploaded correctly.


size file size in bytes (line 8).
PHP stores the uploaded files in a temporary location which is cleaned up as soon as the PHP
script execution ends. So, if you want to save the uploaded files to some directory for later
use, you need to utilize the move_uploaded_file() PHP function. The move_uploaded_file()
function takes two arguments: the first one is the name of the temporary file, and the second one
is the destination file name.
You might be confused why you cannot use the usual rename() PHP function for
moving the temporary uploaded file to its destination path. PHP has special function
for moving uploaded files for security reasons. The move_uploaded_file() function is
analogous to rename() function, but it takes some additional checks to ensure the file
was really transferred through HTTP POST request, and that the upload process has
finished without errors.

The following code example shows how to move the file uploaded with the simple form we have
considered above:
1
2
3
4
5

$destPath = '/path/to/your/upload/dir';
$result = move_uploaded_file($_FILES['myfile']['tmp_name'], $destPath);
if(!$result) {
// Some error occurred.
}

Above, in line 1, we set the the $destPath with the directory name where to save the uploaded
file.
In line 2, we call the move_uploaded_file() function and pass it two arguments: the path to the
temporary file and the destination path.
Specifying the directory name as the second argument of the move_uploaded_file()
function is suitable when you do not want to rename the file. If you need to save the
uploaded file under another name than its original name, you can specify the full file
path instead of the directory name.

In line 3, we check the returned value of the function. If the operation is successful, the function
will return true. If some error occurs (for example, if directory permissions are insufficient to
save the file), the boolean false will be returned.
MIME type, also known as content type is a standard identifier used on the Internet to indicate the type of data that a file contains. For
example the text/plain MIME type is assigned to a text file, while the application/octet-stream MIME type is assigned to a binary file.

Advanced Usage of Forms

319

10.2.3 Accessing Uploaded Files in ZF2


In your controller class, you typically do not communicate with the $_FILES array directly,
instead you may use the Request class or the Params controller plugin, as shown in code example
below:
1
2
3
4
5
6

<?php
//...
class IndexController extends AbstractActionController
{
// An example controller action intended for handling file uploads.
public function uploadAction() {

// Get the whole $_FILES array.


$files = $this->getRequest()->getFiles();

8
9
10

// The same, but with Params controller plugin.


$files = $this->params()->fromFiles();

11
12
13

// Get a single entry of the $_FILES array.


$files = $this->params()->fromFiles('myfile');

14
15

16
17

In line 9 of the code above, we use the getRequest() method of the controller class for accessing
the Request object, and the getFiles() method of the request object to retrieve the information
about all upload files at once.
In line 12, we do the same thing with the Params controller plugin. We use its fromFiles()
method to get the information about all uploaded files.
If needed, you can extract the information for the specific file only. In line 15, we use the same
fromFiles() method and pass it the name of the file field to retrieve. This retrieves the single
file entry from the $_FILES super-global array.

10.2.4 File Uploads & ZF2 Form Model


To add file uploading capability to your form model, you need to add an element of the
Zend\Form\Element\File class as follows:

Advanced Usage of Forms


1

320

// Add the following code inside of form's addElements() method.

2
3
4
5
6
7
8
9
10
11
12
13

// Add the "file" field.


$this->add(array(
'type' => 'file',
'name' => 'file',
'attributes' => array(
'id' => 'file'
),
'options' => array(
'label' => 'Upload file',
),
));

In the code above, we call the add() method provided by the Form base class and pass it the
configuration array describing the element. The type key of the array (line 5) must be either
Zend\Form\Element\File class name or its short alias file.

10.2.5 Validating Uploaded Files


Uploaded files need to be checked for correctness as any other form data. For example, may you
need to check that:
the file(s) were really uploaded through HTTP POST request, and were not just copied
from some directory.
the file(s) were uploaded successfully (the error code is zero);
the file names and/or extensions are acceptable (e.g., you may want to save JPEG files only,
and reject all others);
the file size lies in the allowed range (e.g., you may want to ensure that the file is not to
big);
total count of uploaded files doesnt exceed some allowed limit.
For doing the checks like above, ZF2 provides a number of useful file validators (listed in table
10.2). Those validator classes belong to Zend\Validator component and live in Zend\Validator\File
namespace.

321

Advanced Usage of Forms

Table 10.2. Standard File Validators

Class name

Short alias

Description

Count

FileCount

Checks whether the file count is in a given range


(min, max).

WordCount

FileWordCount

Calculates the number of words in a file and checks


whether it lies in a given range.

Upload

FileUpload

Performs security checks ensuring that all given files


were really uploaded through HTTP POST and there
were no upload errors.

UploadFile

FileUploadFile

Performs security checks ensuring that a file really


was uploaded through HTTP POST and there were
no upload errors.

Size

FileSize

Checks whether the file size lies in a given range.

FilesSize

FileFilesSize

Checks that the summary size of all given files lies in


a given range.

Extension

FileExtension

ExcludeExtension

FileExcludeExtension

MimeType

FileMimeType

Checks that the extension of a file belongs to a set of


allowed extensions.
Checks that the extension of a file DOES NOT belong
to a set of extensions.
Checks that the MIME type of a file belongs to the list
of allowed MIME types.

ExcludeMimeType

FileExcludeMimeType

Checks that the MIME type of a file DOES NOT


belong to the list of MIME types.

IsImage

FileIsImage

Checks that the file is a graphical image (JPEG, PNG,


GIF, etc.)

ImageSize

FileImageSize

Checks that the image files dimensions lie in a given


range.

Exists

FileExists

Checks whether the file exists on disk.

NotExists

FileNotExists

Checks whether the file doesnt exist on disk.

IsCompressed

FileIsCompressed

Checks that the file is an archive (ZIP, TAR, etc.)

Hash

FileHash

Checks that the file content matches the given


hash(es).

Crc32

FileCrc32

Sha1

FileSha1

Checks that the file content has the given CRC32


check sum.
Checks that the file content has the given SHA-1 hash.

Md5

FileMd5

Checks that the file content has the given MD5 hash.

As you can see from the table above, file validators may be roughly divided in the following
groups:

322

Advanced Usage of Forms

validators checking whether the file(s) were really uploaded through HTTP POST and
upload status is OK;
validators checking the uploaded file count and file size;
validators checking the file extension and MIME type;
validators checking whether the file is a graphical image and checking image dimensions;
and validators checking the file hash (or check sum) .

Please note that since file validators live in Zend\Validator\File namespace, their
short aliases (that you use when creating a validator with the factory) start with File
prefix. For example, the IsImage validator has FileIsImage alias.

We will show how to use some of these file validators in the Image Gallery code example later
in this chapter.

10.2.6 Filtering Uploaded Files


Zend Framework 2 provides several filters intended for transforming file fields. Those filter
classes (listed in table 10.1) belong to Zend\Filter component and live in Zend\Filter\File
namespace.
Table 10.1. Standard File Filters

Class name

Short alias

Description

Rename

FileRename

Renames/moves an arbitrary file.

RenameUpload

FileRenameUpload

Renames/moves the uploaded file with security checks.

Encrypt

FileEncrypt

Encrypts a given file and stores the encrypted file content.

Decrypt

FileDecrypt

Decrypts a given file and stores the decrypted file content.

LowerCase

FileLowerCase

Converts file content to lower case letters.

UpperCase

FileUpperCase

Converts file content to upper case letters.

From the table, you can see that filters can be divided into the following groups:
filters for moving uploaded files from a temporary location to their persistent directory;
filters for encryption and decryption of files;
filters for converting text files to upper-case and lower-case letters.
Please note that since file filters live in Zend\Filter\File namespace, their short
aliases (that you use when creating a filter with the factory) start with File prefix.
For example, the RenameUpload filter has FileRenameUpload alias.
A file hash is used for checking file data integrity (for example, to ensure that file data is not corrupted). There are several hash algorithms
available (MD5, SHA-1, CRC32, etc.)

Advanced Usage of Forms

323

The Encrypt and Decrypt filters allow to apply various encryption/decryption algorithms to the
uploaded file (concrete algorithm is attached by specifying the certain adapter). The LowerCase
and UpperCase filters are suitable for converting text files to lower- and upper-case, respectively
.
The Rename filter allows to rename and/or move an arbitrary file (not only uploaded file). It uses
the rename() PHP function internally, and thats why it is in general not recommended to use
this filter with uploaded files because of security reasons.
The RenameUpload filter seems to be much more useful than other filters, because it allows to
encapsulate the call of the move_uploaded_file() function and move/rename the uploaded file
from a temporary location to its persistent directory. We will show how to use the RenameUpload
filter in the Image Gallery code example later in this chapter.

10.2.7 InputFilter Container & File Uploads


As you might remember, the filters and validators attached to a form model are typically stored
in an InputFilter container which consists of inputs (an input is typically represented by the
Input class belonging to the Zend\InputFilter namespace. For usual form fields, the filters are
executed before validators, and validators are executed after filters.
However, for file uploads, there are some important differences:
1. for storing validation rules for uploaded files, a special class called FileInput should be
utilized instead of the Input class;
2. and, validators are applied before filters (!).
10.2.7.1 FileInput
For storing validation rules for uploaded files, you must use the FileInput class instead of the
usual Input class.
In your form models addInputFilter() private method, you add the validation rules for the
file input as follows:
1
2
3
4
5
6
7
8
9
10
11

$inputFilter->add(array(
'type'
=> 'Zend\InputFilter\FileInput',
'name'
=> 'file', // Element's name.
'required' => true,
// Whether the field is required.
'filters' => array(
// Filters.
// Put filter info here.
),
'validators' => array( // Validators.
// Put validator info here.
)
);
In the authors opinion, the above mentioned four filters are not very useful when working with uploaded files, because you rarely need
to encrypt an uploaded file or convert it to lower case letters.

Advanced Usage of Forms

324

Above, we set the type key (line 2) with the value Zend\InputFilter\FileInput class name.
The rest of keys is analogous to those we used before when adding validation rules for a form
model.
The behaviour of FileInput class differs from the Input in the following aspects:
1. It expects the data you pass as input to be in the $_FILES array format (an array entry
with tmp_name, error, type keys).
2. A Zend\Validator\File\Upload validator is automatically inserted before all other validators into the validator chain of the input.
3. The validators inserted to the validator chain of the input are executed before the filters
inserted into its filter chain. This is opposite to the behaviour of the Input class).
10.2.7.2 Executing Validators before Filters
For usual form fields, the filters are typically executed before validators, and validators are
executed after filters. However, for file uploads, this sequence is opposite.
For file uploads, validators are executed before filters. This behaviour is inverse to the
usual behaviour.

When working with uploaded files, we first need to check that data extracted from $_FILES
super-global array is correct, and then do anything else with the files (moving the file into a
storage directory, renaming it, etc.) Because of that, file validators need to be run first turn, and
filters to be executed last.
To see how this is performed, recall the typical workflow for a form:
First, we call the setData() method to fill in the form with data.
Call the isValid() method to execute filters and validators in the input filter attached to
form.
On successful validation, call the getData() to extract the filtered and validated data from
the input filter attached to form.
On failure, call the getMessages() to retrieve the validation error messages.
When using a FileInput input, the workflow is the same, however it is important to understand
what happens on each of its steps:
Call the setData() method to fill in the form with data.
Call the isValid() method to execute validators in the input filter attached to form.
On successful validation, call the getData() to execute filters and extract the filtered and
validated data from the input filter attached to form.
On failure, call the getMessages() to retrieve the validation error messages.

325

Advanced Usage of Forms

Please note that for FileInput input, the attached filters are only run if the getData()
method is called.

When you use both Input and FileInput inputs in your forms input filter (which is a common
case), the filters are still executed first for usual inputs, but validators are executed first for file
inputs.

10.2.8 Example: Image Gallery


To demonstrate the usage of file uploads in Zend Framework 2, we will create an Image Gallery
that will consist of two web pages: the image upload page allowing to upload an image (figure
10.5); and the gallery page containing the list of uploaded images (figure 10.6).
You can see the working Image Gallery example in the Form Demo sample application
bundled with this book.

Figure 10.5. Image Upload Page

326

Advanced Usage of Forms

Figure 10.6. Image Gallery Page

For this example, we will create the following things:

the ImageForm form model capable of image file uploads;


the ImageManager service class designed for getting the list of uploaded images;
the ImageController class which will contain action methods serving the web pages;
a view template .html file per each controllers action method.

10.2.8.1 Adding ImageForm Model


For this example, we will need a form model which will be used for image file uploads. We will
call that form model class the ImageForm. This class will allow us to upload an image file to the
server. The form will have the following fields:
the file field will allow the user to pick the file for upload;
and the submit button will send the form data when user clicks it.
The code of the ImageForm form model is presented below. It should be put to ImageForm.php
file stored in Application/Form directory under the modules source directory:

Advanced Usage of Forms


1
2

<?php
namespace Application\Form;

3
4

use Zend\Form\Form;

5
6
7
8
9
10
11
12
13

// This form is used for uploading an image file.


class ImageForm extends Form
{
// Constructor.
public function __construct()
{
// Define form name
parent::__construct('image-form');

14

// Set POST method for this form.


$this->setAttribute('method', 'post');

15
16
17

// Set binary content encoding.


$form->setAttribute('enctype', 'multipart/form-data');

18
19
20

$this->addElements();

21
22

23
24
25

// This method adds elements to form (input fields and submit button).
protected function addElements() {

26
27
28
29
30
31
32
33
34
35
36
37

// Add "file" field.


$this->add(array(
'type' => 'file',
'name' => 'file',
'attributes' => array(
'id' => 'file'
),
'options' => array(
'label' => 'Image file',
),
));

38
39
40
41
42
43
44
45
46

// Add the submit button.


$this->add(array(
'type' => 'submit',
'name' => 'submit',
'attributes' => array(
'value' => 'Submit',
'id' => 'submitbutton',
),

327

Advanced Usage of Forms

));

47

48
49

328

We have already discussed the form model creation and the code above should not cause any
problems in its understanding. We just want to attract the attention of the reader that in line 19,
we set the multipart/form-data value for the enctype attribute of the form to make the form
use binary encoding for its data.
Actually, explicitly setting the enctype attribute in forms constructor is optional,
because Zend\Form\Element\File element performs that automatically when you call
forms prepare() method.

10.2.8.2 Adding Validation Rules to ImageForm Model


To demonstrate the usage of validators and filters designed to work with file uploads, we will
add those to the ImageForm form model class. We want to achieve the following goals:
check that the uploaded file is an image (JPEG, PNG, GIF, etc.) using the IsImage validator
check that image dimensions are within some allowed boundaries using ImageSize
validator
move the uploaded file to its residence directory using the RenameUpload filter

1
2

<?php
namespace Application\Form;

3
4

use Zend\InputFilter\InputFilter;

5
6
7
8
9
10
11
12

// This form is used for uploading an image file.


class ImageForm extends Form
{
// Constructor
public function __construct()
{
// ...

13

// Add validation rules


$this->addInputFilter();

14
15
16

17
18

// ...

19
20

// This method creates input filter (used for form filtering/validation).

Advanced Usage of Forms

329

private function addInputFilter() {

21
22

$inputFilter = new InputFilter();


$this->setInputFilter($inputFilter);

23
24
25

// Add validation rules for the "file" field


$inputFilter->add(array(
'type'
=> 'Zend\InputFilter\FileInput',
'name'
=> 'file',
'required' => true,
'filters' => array(

26
27
28
29
30
31
32

),
'validators' => array(
array(
'name'
=> 'FileIsImage',
),
array(
'name'
=> 'FileImageSize',
'options' => array(
'minWidth' => 128,
'minHeight' => 128,
'maxWidth' => 4096,
'maxHeight' => 4096
)
),
),

33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

48

);

49

50
51

In the code above, we add the following file validators:


IsImage validator (line 10) checks wether the uploaded file is an image file (PNG, JPG,
GIF, etc.). It does that by extracting MIME information from file data.
ImageSize allows to check that image dimensions are in allowed range. In the code above,
we check that the image is between 128 pixels and 4096 pixels in width, and that the image
is is between 128 pixels and 4096 pixels in height.
For the IsImage validator to work, you have to enable PHP fileinfo extension. This
extension is already enabled in Linux Ubuntu, but in Windows, you have to manually
open the php.ini file and uncomment the following line:
;extension=php_fileinfo.dll

After that, do not forget to restart Apache HTTP Server.

330

Advanced Usage of Forms

10.2.8.3 Writing ImageManager Service


Because we strive to write code conforming to Domain Driven Design pattern, we will create
a service model class encapsulating the functionality for image management. We will call this
class ImageManager and put it to Application\Service namespace. We will also register this
service in the service manager component of the web application.
The ImageManager service class will have the following action methods (listed in table 10.4):
Table 10.4. Action methods of the ImageManager class.

Method

Description

getSaveToDir()

Returns path to the directory where we save the image


files.
Returns the path to the saved image file.
Returns the array of saved file names.
Retrieves the file information (size, MIME type) by
image path.
Returns the image file content. On error, returns
boolean false.
Resizes the image, keeping its aspect ratio.

getImagePathByName($fileName)
getSavedFiles()
getImageFileInfo($filePath)
getImageFileContent($filePath)
resizeImage($filePath, $desiredWidth)

In fact, we could put the code we plan to add into the service into the controller actions
instead, but that would make the controller fat and purely testable. By introducing the
service class, we improve the separation of concerns, code reusability and make the
model layer easily testable.

Add the ImageManager.php file to the Application/Service directory under the modules source
directory. The content of the file is presented below:
1
2

<?php
namespace Application\Service;

3
4
5

// The image manager service.


class ImageManager {

6
7
8

// The directory where uploaded files are stored.


private $uploadDir = './data/upload/';

9
10
11
12
13
14

public function getImagePathByName($fileName)


{
// Take some precautions to Make file name secure
str_replace("/", "", $fileName); // Remove slashes
str_replace("\\", "", $fileName); // Remove back-slashes

15
16
17

// Return concatenated directory name and file name.


return $this->saveToDir . $fileName;

Advanced Usage of Forms


18

331

19
20
21
22

// Returns the array of uploaded file names.


public function getSavedFiles() {
// The directory where we plan to save uploaded files.

23
24
25
26
27
28
29
30
31

// Check whether the directory already exists, and if not,


// create the directory.
if(!is_dir($this->saveToDir)) {
if(!mkdir($this->saveToDir)) {
throw new \Exception('Could not create directory for uploads: '. error\
_get_last());
}
}

32

// Scan the directory and create the list of uploaded files.


$files = array();
$handle = opendir($this->saveToDir);
while (false !== ($entry = readdir($handle))) {
if($entry=='.' || $entry=='..')
continue; // Skip current dir and parent dir.

33
34
35
36
37
38
39

$files[] = $entry;

40

41
42

// Return the list of uploaded files.


return $files;

43
44
45

46
47
48
49
50
51
52
53

// Retrieves the file information (size, MIME type) by image path.


public function getImageFileInfo($filePath)
{
// Try to open file
if (!is_readable($filePath)) {
return false;
}

54
55
56

// Get file size in bytes.


$fileSize = filesize($filePath);

57
58
59
60
61
62
63

// Get MIME type of the file.


$finfo = finfo_open(FILEINFO_MIME);
$mimeType = finfo_file($finfo, $filePath);
if($mimeType===false)
$mimeType = 'application/octet-stream';

Advanced Usage of Forms

return array(
'size' => $fileSize,
'type' => $mimeType
);

64
65
66
67
68

332

69
70
71
72
73
74
75
76
77
78
79

/**
* Returns the image file content. On error, returns boolean false.
* @param string $filePath Path to image file.
* @return string|false
*/
public function getImageFileContent($filePath)
{
return file_get_contents($filePath);
}
}

As you can see from the code above, we have the ImageManager class. It has the private
$uploadDir property which contains the path to the directory containing our uploaded files.
We want to add the image resizing functionality to the ImageManager class that will create
thumbnail images. Add the resizeImage() method to the ImageManager class:
1
2
3
4

<?php
//...
class ImageManager {
//...

5
6
7
8
9
10

// Resizes the image, keeping its aspect ratio.


public function resizeImage($filePath, $desiredWidth = 240)
{
// Get original image dimensions.
list($originalWidth, $originalHeight) = getimagesize($filePath);

11
12
13
14
15

// Calculate aspect ratio


$aspectRatio = $originalWidth/$originalHeight;
// Calculate the resulting height
$desiredHeight = $desiredWidth/$aspectRatio;

16
17
18
19
20
21

// Resize the image


$resultingImage = imagecreatetruecolor($desiredWidth, $desiredHeight);
$originalImage = imagecreatefromjpeg($filePath);
imagecopyresampled($resultingImage, $originalImage, 0, 0, 0, 0,
$desiredWidth, $desiredHeight, $originalWidth, $originalHeight);

22

Although the ImageManager is a service and focused on providing services, it can have properties intended for its internal use.

333

Advanced Usage of Forms

// Save the resized image to temporary location


$tmpFileName = tempnam("/tmp", "FOO");
imagejpeg($resultingImage, $tmpFileName, 80);

23
24
25
26

// Return the path to resulting image.


return $tmpFileName;

27
28

29
30

Finally, you have to register the ImageManager service in service manager by adding the following
lines to the module.config.php configuration file:
<?php
return array(
// ...
'service_manager' => array(
// ...
'invokables' => array(
// Register the ImageManager service
'ImageManager'=>'Application\Service\ImageManager',
),
),
// ...
);

By doing this, you will be able to get a singleton instance of the service in any controller of your
web application as follows:
$imageManager = $this->getServiceLocator()->get('ImageManager');

10.2.8.4 Adding ImageController


For the Image Gallery example, we will create the ImageController controller class. The
controller will have the following action methods (listed in table 10.4):
Table 10.4. Action methods of the ImageController class.

Action Method

Description

ImageController:uploadAction()

Shows the image upload page allowing to upload a single


image.

ImageController:indexAction()

Displays the image gallery page with the list of uploaded


images.

ImageController:fileAction()

Provides an ability to download a full-size image or a small


thumbnail for an image.

Advanced Usage of Forms

334

To start, create the ImageController.php file in the Application/Controller directory under the
modules source directory. Put the following stub code into the file:
1
2

<?php
namespace Application\Controller;

3
4
5
6

use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Application\Form\ImageForm;

7
8
9
10
11
12
13
14
15

// This controller is designed for managing image file uploads.


class ImageController extends AbstractActionController
{
// This is the default "index" action of the controller. It displays the
// Images page which contains the list of uploaded images.
public function indexAction()
{
}

16

// This action shows the image upload form. This page allows to upload
// a single file.
public function uploadAction()
{
}

17
18
19
20
21
22

// This is the 'file' action that is invoked when a user wants to


// open the image file in a web browser or generate a thumbnail.
public function fileAction()
{
}

23
24
25
26
27
28

In the code above, we defined the ImageController class living in the Application\Controller
namespace and added three action method stubs into the class: indexAction() (line 13),
uploadAction() (line 19) and fileAction() (line 25). Next, we will populate those action
methods with the code.
Adding Upload Action & Corresponding View Template
First, we will complete the uploadAction() method of our controller. This action method will
handle the Upload a New Image web page containing the upload form. The form will provide an
ability to upload an image file to the gallery.
Change the ImageController.php file as follows:

Advanced Usage of Forms


1
2
3
4
5
6
7
8
9

335

<?php
//...
class ImageController extends AbstractActionController
{
//...
public function uploadAction()
{
// Create the form model
$form = new ImageForm();

10

// Check if user has submitted the form


if($this->getRequest()->isPost()) {

11
12
13

// Make certain to merge the files info!


$request = $this->getRequest();
$data = array_merge_recursive(
$request->getPost()->toArray(),
$request->getFiles()->toArray()
);

14
15
16
17
18
19
20

// Pass data to form


$form->setData($data);

21
22
23

// Validate form
if($form->isValid()) {

24
25
26

// Move uploaded file to its destination directory.


$data = $form->getData();

27
28
29

// Redirect the user to "Images" page


return $this->redirect()->toRoute('application/default',
array('controller'=>'image', 'action'=>'index'));

30
31
32

33

34
35

// Render the page


return new ViewModel(array(
'form' => $form
));

36
37
38
39

40
41

In the code above, we populated the uploadAction() method with the code.
In line 9, we create an instance of the ImageForm form model with the help of the new operator.
In line 12, we check whether the request is an HTTP POST request. If so, we get the data from

Advanced Usage of Forms

336

$_POST and $_FILES super-global PHP arrays and merge them into the single array (line 13). This

is required to correctly handle uploaded files, if any. Then we pass this array to the form model
with the setData() method (line 22).
In line 25, we call the form models isValid() method. This method runs attached input filter.
Since we have only one file input in the input filter, this will only run file validators: IsImage
and ImageSize. If the data is valid, we call the getData() method (line 28) to run RenameUpload
file filter, which moves our uploaded file to its persistent directory.
After that, in line 31, we redirect the user to the index action of the controller (we will populate
that action method a little bit later.
Now, its time to add the view template for the upload action. Add the upload.html view
template file under the application/image directory under the modules view directory:
1
2
3
4
5

<?php
$form = $this->form;
$form->get('submit')->setAttributes(array('class'=>'btn btn-primary'));
$form->prepare();
?>

6
7

<h1>Upload a New Image</h1>

8
9
10
11

<p>
Please fill out the following form and press the <i>Upload</i> button.
</p>

12
13
14
15

<div class="row">
<div class="col-md-6">
<?php echo $this->form()->openTag($form); ?>

16
17
18
19
20
21

<div class="form-group">
<?php echo $this->formLabel($form->get('file')); ?>
<?php echo $this->formElement($form->get('file')); ?>
<?php echo $this->formElementErrors($form->get('file')); ?>
</div>

22
23

<?php echo $this->formElement($form->get('submit')); ?>

24
25
26
27

<?php echo $this->form()->closeTag(); ?>


</div>
</div>

In the code of the view template, we first set class attribute on the form (this is to apply nicelooking Twitter Bootstrap styles to the forms Submit button).
Then, we render the form with the common view helpers that we discussed in Chapter 7. For
rendering the file field, we use the generic FormElement view helper.

Advanced Usage of Forms

337

Typically, you use the FormElement generic view helper for rendering the file field.
The FormElement internally calls the FormFile view helper, which performs the actual
rendering.

Adding Index Action & Corresponding View Template


The second action method we will complete is the indexAction(). This action will handle the
Image Gallery page containing the list of uploaded files and their small thumbnails. For each
image, there will be a button View in Full Size for opening the image in another tab of the web
browser.
Change the ImageController.php file as follows:
1
2
3
4
5
6
7
8
9

<?php
//...
class ImageController extends AbstractActionController
{
//...
public function indexAction()
{
// Get the singleton of the image manager service.
$imageManager = $this->getServiceLocator()->get('ImageManager');

10

// Get the list of already saved files.


$files = $imageManager->getSavedFiles();

11
12
13

// Render the view template


return new ViewModel(array(
'files'=>$files
));

14
15
16
17

18
19

Add the index.phtml view template to image/index directory under the modules view directory.
The contents of the file is shown below:
1

<h1>Image Gallery</h1>

2
3
4
5

<p>
This page displays the list of uploaded images.
</p>

6
7
8
9
10

<p>
<a href="<?php echo $this->url('application/default', array('controller'=>'i\
mage', 'action'=>'upload')); ?>"
class="btn btn-primary" role="button">Upload More</a>

338

Advanced Usage of Forms


11

</p>

12
13

<hr/>

14
15

<?php if(count($files)==0): ?>

16
17
18
19

<p>
<i>There are no files to display.</i>
</p>

20
21

<?php else: ?>

22
23
24

<div class="row">
<div class="col-sm-6 col-md-12">

25

<?php foreach($files as $file): ?>

26
27

<div class="img-thumbnail">

28
29

<img src="<?php echo $this->url('application/default',


array('controller'=>'image', 'action'=>'file'),
array('query'=>array('name'=>$file, 'thumbnail'=>true))); ?>\

30
31
32
33

">

34

<div class="caption">
<h3><?php echo $file; ?></h3>
<p>
<a target="_blank" href="<?php echo $this->url('application/\

35
36
37
38
39

default',
array('controller'=>'image', 'action'=>'file'),
array('query'=>array('name'=>$file))); ?>"
class="btn btn-default" role="button">Show in Natura\

40
41
42
43

l Size</a>
</p>
</div>
</div>

44
45
46
47
48
49
50

<?php endforeach; ?>


</div>
</div>

51
52

<?php endif; ?>

53
54

<hr/>

In the code above, we create the HTML markup for the Upload More button.

Advanced Usage of Forms

339

Under the button, we output the thumbnails of the uploaded images. We use the Twitter
Bootstrap provided .img-thumbnail CSS class.
For additional information about these Twitter Bootstrap styles, please refer to the
Bootstrap official documentation.

Adding File Action


The last action we populate will be the ImageController::fileAction() method. That method
will allow to preview an uploaded image or generate a small thumbnail of the image. The action
method will take two GET parameters:
the name parameter defines the file name for preview;
the thumbnail parameter is a flag telling whether we want to dump the full image or its
small copy.
Change the ImageController.php file as follows:
1
2
3
4
5
6
7
8
9

<?php
//...
class ImageController extends AbstractActionController
{
//...
public function fileAction()
{
// Get the file name from GET variable
$fileName = $this->params()->fromQuery('name', '');

10
11
12

// Check whether the user needs a thumbnail or a full-size image


$isThumbnail = (bool)$this->params()->fromQuery('thumbnail', false);

13
14
15

// Get the singleton of the image manager service.


$imageManager = $this->getServiceLocator()->get('ImageManager');

16
17
18

// Get path to image file


$fileName = $imageManager->getImagePathByName($fileName);

19
20

if($isThumbnail) {

21

// Resize the image


$fileName = $imageManager->resizeImage($fileName);

22
23
24

25
26
27

// Get image file info (size and MIME type).


$fileInfo = $imageManager->getImageFileInfo($fileName);

Advanced Usage of Forms

340

if ($fileInfo===false) {
// Set 404 Not Found status code
$this->getResponse()->setStatusCode(404);
return;
}

28
29
30
31
32
33

// Write HTTP headers.


$response = $this->getResponse();
$headers = $response->getHeaders();
$headers->addHeaderLine("Content-type: " . $fileInfo['type']);
$headers->addHeaderLine("Content-length: " . $fileInfo['size']);

34
35
36
37
38
39

// Write file content


$fileContent = $imageManager->getImageFileContent($fileName);
if($fileContent!==false) {
$response->setContent($fileContent);
} else {
// Set 500 Server Error status code
$this->getResponse()->setStatusCode(500);
return;
}

40
41
42
43
44
45
46
47
48
49

if($isThumbnail) {
// Remove temporary thumbnail image file.
unlink($fileName);
}

50
51
52
53
54

// Return Response to avoid default view rendering.


return $this->getResponse();

55
56

57
58

In the code above, we first get the name and thumbnail parameters from $_GET super-global
array (lines 5, 7). If the parameters are missing, their default values are used instead.
Then, in line 9, we retrieve a singleton instance of our ImageManager service that we previously
registered in the service manager.
In line 11, we use the getImagePathByName() method provided by the ImageManager service to
get the absolute path to the image by its name.
If a thumbnail is requested, we resize the image with the resizeImage() method of the
ImageManager (line 13). That method returns path to a temporary file containing the thumbnail
image.
Then, we get the information about the image file (its MIME type and file size) with the
getImageFileInfo() method of the ImageManager (line 15).

341

Advanced Usage of Forms

Finally, we create a Response object, fill its headers with image information (lines 20-25), set its
content with data of the image file, and return the Response object from the controller action.
Note that returning the Response object disables the default rendering of the view
template for this action method. By this reason, we do not create the file.phtml view
template file.

Results
If you now enter the URL http://localhost/application/image/index into your web browsers
navigation bar, you will see the image gallery page like shown in figure 10.8.

Figure 10.8. Image Gallery Page

Clicking the Upload More button will open the Upload a New Image page where you can peek
an image file for upload. If you pick an unacceptable file (not an image, or too big image), you
will see validation errors.
If the upload is completed successfully, you will be redirected back to the Image Gallery page
and see the uploaded image in the list of thumbnails. Clicking the View Full Size button will open
the image in a new browser tab.
You may find the Image Gallery complete example in the Form Demo sample web
application bundled with this book.

Advanced Usage of Forms

342

This chapter is currently being developed and is not yet incomplete. You will get all
updates for free as they appear. If you see some mistake, please contact the author using
the following E-mail address: olegrkrivtsov@gmail.com. The author will be more than
happy to answer you and fix the mistake.

10.3 Summary
In this chapter, we have discussed some advanced form usage capabilities.
Zend Framework 2 provides two classes whose purpose is enhancing form security: Captcha and
Csrf. A CAPTCHA is a type of challenge-response test used to determine whether or not the user
is a human. CAPTCHA elements are used on form to prevent form submission by a malicious
automated process (a robot). The latter element, Csrf, is used for Cross-Site Request Forgery
(abbreviated as CSRF) hacker attack prevention.
File uploads is a standard HTML form feature. Uploading files is accomplished by setting form
content encoding to binary encoding type. Zend Framework 2 provides convenient functionality
for doing file uploads and validating the uploaded files.

Appendix A. Configuring Web


Development Environment
Here we will provide instructions on how to prepare your environment for developing ZF2-based
applications. If you already have the configured environment, you can skip this.
Please also consider browsing the tutorials online. Those tutorials provide step-bystep instructions on how to install a ZF2-based web application to Amazon EC2 cloud
and to PHPCloud service.

Configuring the development environment is the first thing you have to do when beginning with
creating a new web-site. This includes installing a web server, the PHP engine with required
extensions and a database.
For the purpose of running the code examples created in this book, we will use Apache HTTP
Server (v.2.2 or later), PHP engine (v.5.3 or later) with XDebug extension and MySQL database
(v.5.5 or later).
We also provide instructions for installing NetBeans IDE, which is a convenient integrated
development environment for PHP development. It allows for an easier navigation, editing and
debugging of your PHP application. The NetBeans IDE is written in Java and can be installed in
Windows, Linux and other platforms supporting a compatible Java machine.

Installing Apache, PHP and MySQL in Linux


In general, it is recommended that you use a popular and well supported Linux distribution,
either 32-bit (x86) or 64-bit (amd64). A 64-bit version can give a great performance, but deliver
more problems (like driver compatibility issues). 32-bit systems are with us for a longer time and
have less problems, which is important for novice users.
There are two big families of Linux distributions: Debian and Red Hat. Debian is a free and
open-source project, which has several branches, the most popular of which is Linux Ubuntu.
Red Hat is a commercially distributed operating system, which has free branches named Linux
CentOS and Linux Fedora.
Red Hat Linux is being developed by Red Hat Inc. Red Hat Linux (or its free modification
CentOS) is known as a corporate operating system. Its main advantage is stability (low rate
of system crashes). However, this stability is achieved through carefully choosing the software
which is installed out of the box. When you install such an operating system for the purpose
of PHP development, this stability may become a problem, because you have access to some
old (but stable) version of PHP and other software. They do not include a new bleeding-edge
http://using-zend-framework-2-book.com/tutorials/

344

Appendix A. Configuring Web Development Environment

software into their repository, so if you want to install one, you will need to download it from
somewhere, read the manual, and possibly (if you are not lucky) compile it yourself.
There is another Linux distribution, which, in the authors opinion, suits better for PHP
development. Its name is Linux Ubuntu. Ubuntu is being developed by Canonical Ltd. Linux
Ubuntu has two editions: Desktop edition and Server edition. Ubuntu Desktop is a distribution
containing graphics environment, while Ubuntu Server edition has console terminal only. For
the purpose of PHP development, you will need Desktop edition.
Canonical typically releases a new version of Linux Ubuntu each 6 months, in April and October,
and a long term support (LTS) version each 2 years. For example, at the moment of writing
this text, the latest version is Ubuntu 13.04 Raring Ringtail (released in April 2013). The latest
LTS release is Ubuntu 12.04 Precise Pangolin (released in April 2012).
Non-LTS releases have short support period (about 9 months), but they have the newest versions
of the PHP software out of the box. On the other hand, LTS releases have longer support period
(5 years), but a little outdated PHP software out of the box. For PHP development, the author
would recommend to use the latest non-LTS version of Ubuntu Desktop, because it has the newest
version of PHP and other software available from repository. The disadvantage of using such a
version is that you will need to upgrade it to the next release every 9 months (as support period
expires).
For your information, table A.1 lists PHP versions avaiable for installation from repository in
different Linux distributions:
Table A.1. Available PHP versions in different Linux distributions

Linux Distribution

PHP Version

Linux Ubuntu 13.04 Raring Ringtail


Linux Ubuntu 12.10 Quantal Quetzal
Linux Ubuntu 12.04 Precise Pangolin LTS
Linux Debian 7.0 Wheezy
Linux Debian 6.0 Squeeze
Red Hat Enterprise Linux 6.4
Linux Cent OS 6.4
Linux Fedora 19
Linux Fedora 18

5.4.9
5.4.6
5.3.10
5.4.4
5.3.3
5.3.3
5.4.15
5.5.0
5.4.9

When choosing between 32-bit and 64-bit versions of the system, remember that the 64 bit
version of Linux Ubuntu will have more compatibility issues than its 32-bit counterpart. The
support of drivers can also cause problems on the 64-bit platform.

Installing Apache and PHP


In modern Linux distributions, you can easily download and install software from a centralized
repository. The repository contains so called packages. A package has a name (for example, php5,
apache2), and a version.
In general you can install a package with a single command. However the command (and a
package name) may differ based on linux distribution you use. For example, to download and

Appendix A. Configuring Web Development Environment

345

install packages in Debian-based Linux distributions (e.g. Ubuntu Linux), you use Advanced
Packaging Tool (APT). In Red Hat provided distributions (e.g. Fedora or CentOS), you use YUM
(RPM package manager). Below, detailed installation instructions for these operating systems are
provided.
Debian or Linux Ubuntu
First of all, it is recommended that you update your system by installing the latest available
updates. To do this, from a command shell, run the following commands:
sudo apt-get update
sudo apt-get upgrade

The commands above runs the APT tool and install the newest system packages updates. The
sudo command (stands for Super User DO) allows to run another command, apt-get in our
case, as system administrator (root). You typically use sudo when you need to elevate your
privileges to install a package or edit some configuration file.
The sudo command may request you for password. When prompted, enter the password under which you log into the system and press Enter.

Next, from a command shell, run the following commands:


sudo apt-get install apache2
sudo apt-get install php5
sudo apt-get install libapache2-mod-php5

The commands above download from repository and install the latest available versions of
Apache HTTP Server, PHP engine and PHP extension module for Apache.
The commands above may ask you for confirmation when installing a package. It is
recommended to answer Yes (press y and then press Enter).

Fedora, CentOS or Red Hat Linux


First of all, it is recommended that you update your system by installing the latest available
updates. To do this, from a command shell, run the following command:
sudo yum update

The command above runs the YUM tool and installs the newest system package updates.
Next, from a command shell, run the following commands:
sudo yum install httpd
sudo yum install php

The commands above download from repository and install the latest available versions of
Apache HTTP Server and PHP engine.
Next, run the following commands to add Apache HTTP Server to system autorun and start it:

Appendix A. Configuring Web Development Environment

346

sudo chkconfig --level 235 httpd on


sudo service httpd start

Checking Web Server Installation


After you set up your Apache HTTP web server, check that it is installed correctly and that
the server sees the PHP engine. To do that, create phpinfo.php file in Apache document root
directory.
The document root is a directory where you can (by default) store the web files. Typically, the
Apache document root directory is /var/www (in Debian or Linux Ubuntu) or /var/www/html
(in Fedora, CentOS or Red Hat Linux).
To be able to navigate the directory structure and edit files, it is recommended to install
Midnight Commander (convenient file manager and text editor). To install Midnight
Commander in Debian or Linux Ubuntu, type the following:
sudo apt-get install mc

The following command installs Midnight Commander in Fedora, CentOS or Red Hat
Linux:
sudo yum install mc

After installation, you can edit a text file with the command like this:
mcedit /path/to/file

If you need administrative permissions to edit the file, prepend the sudo command to
the command above.

In the phpinfo.php file, enter the PHP method phpinfo() as follows:


<?php
phpinfo();
?>

Open the file in your web browser. The standard PHP information page should display (see figure
A.1 for example).

Editing PHP Configuration


To configure PHP for your development environment, you need to edit the PHP config file
(php.ini) and adjust some parameters.
In different distributions of Linux, PHP configuration file can be located in different
paths. To edit the PHP config file in Debian or Linux Ubuntu, type the following:
sudo mcedit /etc/php5/apache2/php.ini

Type the following to edit php.ini in Fedora, CentOS or Red Hat Linux:
sudo mcedit /etc/php.ini

Appendix A. Configuring Web Development Environment

347

Figure A.1. PHP Information

For the development environment, it is recommended to set the following error handling and
logging parameters as below. This will force PHP to display errors on your PHP pages to screen.
error_reporting = E_ALL
display_errors = On
display_startup_errors = On

To conveniently search within the file, press F7 in Midnight Commanders editor


window and enter the search string (the name of the parameter to search for).

Set your time zone settings (replace <your_time_zone> placeholder with your time zone, for
example, UTC or America/New_York):
date.timezone = <your_time_zone>

Set max_execution_time, upload_max_filesize and post_max_size parameters to allow large


file uploads through POST. For example, setting upload_max_filesize with 128M allows to
upload files up to 128 megabytes in size. Setting max_execution_time with zero allows to execute
the PHP script indefinitely long.
max_execution_time = 0

348

Appendix A. Configuring Web Development Environment

post_max_size=128M
upload_max_filesize=128M

When ready, save your changes by pressing the F2 key and then press F10 to exit from Midnight
Commanders editor.

Restarting Apache Web Server


After editing configuration files, you usually have to restart Apache HTTP Server to apply your
changes. You do this with the following command (in Debian or Linux Ubuntu):
sudo service apache2 restart

or the following (in Fedora, CentOS or Red Hat):


sudo service httpd restart

As a result, you should see output like below:


* Restarting web server apache2
apache2: Could not reliably determine the server's fully qualified
domain name, using 127.0.0.1 for ServerName
... waiting apache2: Could not reliably determine the server's fully
qualified domain name, using 127.0.0.1 for ServerName

[OK]

Enabling Apaches mod_rewrite module


Zend Framework 2 requires that you have Apaches mod_rewrite module enabled. The mod_rewrite module is used to rewrite requested URLs based on some rules, redirecting site users to
another URL.
In Debian or Ubuntu Linux
To enable Apache mod_rewrite module, type the following commands:
cd /etc/apache2/mods-enabled
sudo ln -s ../mods-available/rewrite.load ./rewrite.load

The command above creates a symbolic link in the mods-enabled directory, this way you enable
modules in modern versions of Apache web server.
Finally, restart Apache web server to apply your changes.
A symbolic link in Linux is an analog of a shortcut in Windows.

In Fedora, CentOS or Red Hat Linux


In these Linux distributions, mod_rewrite is enabled by default, so you dont need to do anything.

Appendix A. Configuring Web Development Environment

349

Creating Apache Virtual Host


Zend Framework 2 requires that you create a virtual host for your web site. A virtual host term
means that you can run several web-sites on the same machine.
The virtual sites are differentiated by domain name (like site.mydomain.com and site2.mydomain.com).
Each virtual host has its own document root directory, allowing you to place your web files
anywhere on the system (not only to /var/www or /var/www/html directory).
Please note that right now you dont need to create a virtual host, well do that in
Chapter 2 when installing the Zend Skeleton Application. Now you just need to have
an idea of how virtual hosts are created in different Linux distributions.

In Debian or Ubuntu Linux


You have an example default virtual host at /etc/apache2/sites-enabled/000-default (see below).
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/
<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
<Directory /var/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
</VirtualHost>

All you have to do is just to edit this virtual host file when needed and restart Apache to apply
changes.
You can also copy this file and create another virtual host, when you need several web sites to
operate on the same machine. For example, to create another virtual host file named vhost2 ,
type the following from your command shell:
cd /etc/apache2/sites-available

Appendix A. Configuring Web Development Environment

350

sudo cp default vhost2


cd ../sites-enabled
sudo ln -s ../sites-available/vhost2 ./010-vhost2

The virtual hosts name starts with a prefix (like 000, 010, etc.), which defines the
priority. Apache web server tries to direct an HTTP request to each virtual host in
turn (first to 000-default, then to 010-vhost2), and if a certain virtual host cannot serve
the request, the next one is tried and so on.

In Fedora, CentOS or Red Hat Linux


There is an example virtual host in /etc/httpd/conf/httpd.conf file. Scroll down to the very bottom
of the document to the section called Virtual Hosts. You can edit this section as you need and
restart Apache to apply your changes.

Installing XDebug PHP extension


To be able to debug your web sites, it is recommended that you install the XDebug extension. The
XDebug extension allows to look inside a running program, see the variables passed from the
client, walk the call stack and profile your PHP code. XDebug also provides the code coverage
analysis capabilities, which are useful when you write unit tests for your code.
In Debian or Ubuntu Linux
To install XDebug, simply type the following command:
sudo apt-get install php5-xdebug

Then edit the 20-xdebug.ini file by typing the following:


sudo mcedit /etc/php5/apache2/conf.d/20-xdebug.ini

Add the following lines:


xdebug.remote_enable=1
xdebug.remote_handler=dbgp
xdebug.remote_mode=req
xdebug.remote_host=127.0.0.1
xdebug.remote_port=9000
xdebug.profiler_enable = 1

Finally, restart the Apache server to apply your changes. Then open the phpinfo.php in your
browser and look for XDebug section (it should look like in the figure A.2):
In Fedora, CentOS or Red Hat Linux
In these Linux distributions, installing XDebug is a little more difficult. Install XDebug package
with the following command:
yum install php-pecl-xdebug

Appendix A. Configuring Web Development Environment

351

After install, it is required to create the file xdebug.ini in /etc/php.d directory:


mcedit /etc/php.d/xdebug.ini

In that file, add the following line:


zend_extension = /usr/lib/php/modules/xdebug.so

Edit your php.ini file


mcedit /etc/php.ini

and add the following section:

Figure A.2. XDebug Information

[xdebug]
xdebug.remote_enable=1
xdebug.remote_handler=dbgp
xdebug.remote_mode=req
xdebug.remote_host=127.0.0.1
xdebug.remote_port=9000
xdebug.profiler_enable = 1

Restart the Apache web server to apply your changes. Then check the phpinfo.php in your
browser. If installation was successfull, youll see some XDebug-related information.

Appendix A. Configuring Web Development Environment

352

Installing APC PHP Extension


The Alternative PHP Cache (APC) is an opcode cache for PHP. It is designed to provide your
PHP web sites with the ability of caching and optimizing the PHP intermediate code generated
by the PHP interpreter. Although this extension is optional, it is recommended that you install
it to speed up the operation of your ZF2-based web sites.
To install APC extension in Linux Ubuntu, type the following from your command shell:
sudo apt-get install php-apc

To do that in Red Hat, CentOS or Fedora, type the following line:


sudo yum install php-apc

Finally, restart Apache web server to apply the changes.

Installing MySQL Database Server


MySQL is a free relational database management system being developed and supported by
Oracle. MySQL is the most popular database system used with PHP.
Debian or Linux Ubuntu
In order to install MySQL database, type the following:
sudo apt-get install mysql-server
sudo apt-get install mysql-client
sudo apt-get install php5-mysql

The commands above install MySQL server component, MySQL client component and MySQL
extension module for PHP, respectively.
Fedora, CentOS or Red Hat Linux
In order to install MySQL database, type the following:
sudo yum install mysql-server
sudo yum install mysql
sudo yum install php-mysql

The commands above install MySQL server component, MySQL client component and MySQL
extension module for PHP, respectively.
Run the following commands to add MySQL server to autostart and start the server:
sudo chkconfig --level 235 mysqld on
sudo service mysqld start
http://www.mysql.com/

Appendix A. Configuring Web Development Environment

353

Configuring the MySQL Database Server


During the installation of the MySQL server, a root user is created. By default the root user has
no password, so you have to set it manually. You will need that password for creating other
MySQL database users.
To connect to the MySQL server enter the following command:
mysql -u root -p

The MySQL command prompt will appear. In the command prompt enter the following
command and press Enter (in the command below, replace the <your_password> placeholder
with some password):
SET PASSWORD FOR 'root'@'localhost' = PASSWORD('<your_password>');

If the command is executed successfully, the following message is displayed:


Query OK, 0 rows affected (0.00 sec)

Now we need to create a new database schema that will store the tables. To do this, type the
following:
CREATE SCHEMA test_db;

The command above creates empty schema that we will populate later.
Next, we want to create another database user named test_user that will be used by the ZF2based web site for connecting to the database. To create the user, type the following (in the
command below, replace the <your_password> placeholder with some password):
GRANT ALL PRIVILEGES ON test_db.* TO 'test_user'@'localhost' IDENTIFIED BY '<your_password>';

The command above creates the user named test_user and grants the user all privileges on the
test_db database schema.
Finally, type quit to exit the MySQL prompt.

Installing Apache, PHP and MySQL in Windows


Download the latest stable version (current stable version 2.2 recommended) of Apache HTTP
Server from Apache Download Page. On that page, look for the Apache HTTP Server 2.2.24
(httpd) section (see the figure A.3), under that section click Other files link.
http://httpd.apache.org/download.cgi

Appendix A. Configuring Web Development Environment

354

Figure A.3. Apache HTTP Server download page

We need to download the binary installer for Windows, so on the page that appears, click binaries
link (see the figure A.4).

Figure A.4. Binaries folder

On the next page click win32 link. In this page, look for httpd-2.2.22-win32-x86-openssl-0.9.8t.msi
download link and click it to download the MSI installer (see the figure A.5).

Appendix A. Configuring Web Development Environment

355

Figure A.5. Apache HTTP Server MSI installer

Run the installer and follow the instructions of the wizard. If everything is OK, you should see
the Apache icon in the system tray (see the figure A.6).

Figure A.6. Apache icon in system tray

Next, download the current stable version (version 5.5 recommended) of PHP from PHP web
site. Click the Windows 5.5.0 binaries and source link and download VC11 x86 Thread Safe ZIP
archive. Then unpack the archive to C:\Program Files (x86)\PHP.
In order to enable PHP engine, you have to edit the web servers configuration file (httpd.conf ).
Typically, the Apache config file is located in C:Program Files (x86)Apache Software FoundationApache2.2\conf folder. You need to add the following lines to your Apache httpd.conf
configuration file to load the PHP-Module for Apache 2.2:
# Load PHP module
LoadModule php5_module "C:/Program Files (x86)/php/php5apache2_2.dll"
AddHandler application/x-httpd-php .php
# Specify path to php.ini
PHPIniDir "C:/Program Files (x86)/php"

After editing the config file, restart Apache. You can do this by clicking the Apache tray icon
and choosing Restart from the context menu.
In Windows Vista/Windows 7, httpd.conf file permissions may prevent you from
saving the changes to file. To be able to save your changes to httpd.conf, you should
run your editor as administrator. For example, if you use notepad editor, you should
run it as administrator by right-clicking its icon and choosing Run as administrator
from context menu.
http://www.php.net/downloads.php

Appendix A. Configuring Web Development Environment

356

Checking Web Server Installation


After you set up your web server, check that it is installed correctly and that your Apache server
recognizes the PHP engine.
To check that Apache and PHP are installed correctly, create phpinfo.php file in Apache
document root directory.
Typically, the Apache document root directory is C:Program Files (x86)Apache Software
FoundationApache2.2\htdocs.

In Windows Vista/Windows 7, htdocs directory permissions may prevent you from


creating files in it. A workaround for this is to modify directory permissions. Rightclick the htdocs directory name, choose Properties from context menu. Properties dialog
appears (see the figure A.7). Click Security tab, then in the list select Users and click
Edit button. In the appered dialog, set check on Full control checkbox and click OK
button.

Figure A.7. Directory Permissions Dialog

In the phpinfo.php file, enter the PHP method phpinfo() as follows:

Appendix A. Configuring Web Development Environment

357

<?php
phpinfo();
?>

Open the file in your browser. The standard PHP information page should display (figure A.8).

Figure A.8. PHP Information

Enabling Apaches mod_rewrite module


Zend Framework 2 requires that you have Apaches mod_rewrite module enabled. To enable
mod_rewrite, open your the php.ini config file, then find the following line:
#LoadModule rewrite_module modules/mod_rewrite.so

and remove the hash (#) sign from the beginning to uncomment the line. It should now look like
this:

Appendix A. Configuring Web Development Environment

358

LoadModule rewrite_module modules/mod_rewrite.so

Finally, restart Apache web server to apply your changes.

Creating Apache Virtual Host


A virtual host term means that you can run several web-sites on the same machine. The virtual
sites are differentiated by domain name (like site.mydomain.com and site2.mydomain.com)
Virtual hosts are disabled by default. To enable this feature, open C:Program FilesApache
Software FoundationApache2.2\conf\httpd.conf config file in a text editor. Scroll down the file,
and locate the following section:
#Virtual hosts
#Include conf/extra/httpd-vhosts.conf

Uncomment the second line, so it looks like this:


#Virtual hosts
Include conf/extra/httpd-vhosts.conf

The line you uncommented forces Apache to include the additional config file named httpdvhosts.conf. Open C:Program FilesApache Software FoundationApache2.2\conf\extra\httpd-vhosts.conf
in a text editor. This file contains an example virtual host configuration (see below).
#
# Use name-based virtual hosting.
#
NameVirtualHost *:80
#
# VirtualHost example:
# Almost any Apache directive may go into a VirtualHost
# container. The first VirtualHost section is used for
# all requests that do not match a ServerName or ServerAlias
# in any <VirtualHost> block.
#
<VirtualHost *:80>
ServerAdmin webmaster@dummy-host.localhost
DocumentRoot "C:/Program Files (x86)/Apache Software Foundation\
/Apache2.2/htdocs"
ServerName dummy-host.localhost
ServerAlias www.dummy-host.localhost
ErrorLog "logs/dummy-host.localhost-error.log"
CustomLog "logs/dummy-host.localhost-access.log" common
</VirtualHost>

Appendix A. Configuring Web Development Environment

359

You can edit it as needed and restart Apache to apply the changes.
Right now, you dont need to edit this file, well do that in Chapter 2 when installing
the Hello World application. Now you just need to understand how to create virtual
hosts.

Installing XDebug PHP extension


To be able to debug your web sites in NetBeans IDE, it is recommended that you install the
XDebug extension of your PHP engine. Download an appropriate DLL from this site. For
example, if you have installed PHP 5.5, you should download php_xdebug-2.2.3-5.5-vc11.dll
Then edit your php.ini file and add the following section:
zend_extension="C:\Program Files (x86)\PHP\ext\php_xdebug-2.2.3-5.5-vc11.dll"
xdebug.remote_enable=on
xdebug.remote_handler=dbgp
xdebug.remote_host=localhost
xdebug.remote_port=9000
xdebug.profiler_enable = 1

Finally, restart the Apache server to apply your changes. Then open the phpinfo.php in your
browser and look for XDebug section (it should look like in the figure A.9):
http://www.xdebug.org/download.php

Appendix A. Configuring Web Development Environment

360

Figure A.9. XDebug Information

Installing MySQL Database Server


Download the latest version of MySQL Community Server from this page. On the download
page, choose Windows (x86, 32-bit), MSI Installer.
Run the MySQL installer and follow the instructions of the wizard.

Configuring the MySQL Database Server


Now we want to create a database schema and a database user. We will use MySQL Command
Line Client. You can launch the client from Start Menu | All Programs | MySQL | MySQL Server
5.x | MySQL 5.x Command Line Client. The MySQL Command Line Client console appears (see
the figure A.10):
http://www.mysql.com/downloads/

Appendix A. Configuring Web Development Environment

361

Figure A.10. MySQL Command-Line Client

If you prefer to administer MySQL server using a graphical tool, you can download
and install MySQL Workbench (GUI Tool) from MySQL download page.

Now we need to create a new database schema that will store the tables. To do this, type the
following in the MySQL client window:
CREATE SCHEMA test_db;

The command above creates an empty database schema that we will populate later. If the
command is executed successfully, the following message is displayed:
Query OK, 1 rows affected (0.05 sec)

Next, we want to create another database user named test_user that will be used by the web
site for connecting to the database. To create the user, type the following (in the command below,
replace the <your_password> placeholder with some password):
GRANT ALL PRIVILEGES ON test_db.* TO 'test_user'@'localhost' IDENTIFIED BY '<your_password>';

The command above creates the user named test_user and grants the user all privileges on the
test_db database schema.
If you found the installation procedure described above to be boring, you might want
to use an integrated solution (stack) for installing Apache HTTP Server, PHP engine
and MySQL database server using a single click. For an example of such an integrated
solution, visit the WAMP Server page.

http://www.wampserver.com/en/

Appendix A. Configuring Web Development Environment

362

Installing NetBeans IDE in Linux


You can install NetBeans IDE using two methods: either from repository, as you did with Apache,
PHP and MySQL, or by downloading the installer from NetBeans web site and running it. The
first method is simpler, but it may give you an outdated version of NetBeans, depending on your
Linux distribution. The second method is a little more complicated, but it gives you the most
up-to-date version of the IDE. The latter one is recommended.

Method 1. Installing from Repository


To install NetBeans IDE in Debian or Linux Ubuntu, type the following command from your
command shell:
sudo apt-get install netbeans

or the following command to install it in Fedora, CentOS or Red Hat Linux:


sudo yum install netbeans

The command above downloads from repository and installs NetBeans and all its dependent
packages. After the installation is complete, you can run netbeans by typing:
netbeans

The NetBeans IDE window is shown in figure A.11.

Figure A.11. NetBeans IDE

Appendix A. Configuring Web Development Environment

363

To be able to create PHP projects, you need to activate PHP plugin for NetBeans. To do that,
open menu Tools->Plugins, the Plugins dialog appears. In the appeared dialog, click Settings
tab and set check marks to all Configuration and Update Centers (see the figure A.12).
Then click the Available Plugins tab. On that tab, click the Reload Catalog button to build the
list of all available plugins. Then in the list, set check mark to PHP plugin and click the Install
button (see the figure A.13).
When the PHP plugin installation is complete, restart the IDE. Then you should be able to create
new PHP projects from menu New->New Project....
It is also recommended to update NetBeans IDE to the latest version by opening menu
Help->Check for updates.

Method 2. Downloading from Web Site


Because NetBeans is written in Java, we have to first install Java virtual machine (JVM). JVM
can be installed from an external Oracle-provided repository. To let your system know about the
repository, run the following commands from your command shell:
sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update

To install Java 7 (the latest stable version at the moment), run following command:
sudo apt-get install oracle-java7-installer

To ensure that Java 7 has been installed successfully, run the following command from your
command shell:
java -version

If everything is OK, you should see something like below:


java version "1.7.0_25"
Java(TM) SE Runtime Environment (build 1.7.0_25-b15)
Java HotSpot(TM) Client VM (build 23.25-b01, mixed mode)

Next, download NetBeans installer from the NetBeans site (figure A.14). You may encounter
several NetBeans bundles available for download. Download the distribution that is intended for
PHP development.
https://netbeans.org/

Appendix A. Configuring Web Development Environment

Figure A.12. NetBeans Plugins

Figure A.13. NetBeans Plugins

364

Appendix A. Configuring Web Development Environment

365

Figure A.14. Download NetBeans PHP bundle

When download has been finished, go to your Downloads directory and run the installer by
typing the commands below:
cd /Downloads
sudo chmod +x netbeans-7.3.1-php-linux.sh
./netbeans-7.3.1-php-linux.sh

Please note, that the installers file name may be different, depending on your version
of NetBeans.

When the installation is completed, you can run NetBeans by typing netbeans in the command
line. You should be able to see NetBeans splash screen, and then the IDE main window should
appear.

Appendix A. Configuring Web Development Environment

366

Installing NetBeans IDE in Windows


Installing NetBeans in Windows is strightforward. You just need to download the installer
from NetBeans site and run it. You may encounter several bundles of NetBeans available for
download, and you should download the bundle that is intended for PHP development (see the
figure A.14 for example).

Figure A.14. NetBeans PHP Download Page

If your machine does not contain Java Runtime Environment (JRE), you should
download and install it before NetBeans installation. Java JRE can be downloaded from
Oracle site.

Summary
In this appendix, weve provided instructions on how to install and configure Apache HTTP
Server, PHP engine and MySQL database in both Linux and Windows platforms.
Weve also provided instructions for installing NetBeans integrated development environment
(IDE), which is a convenient integrated development environment for PHP development. It
allows you to navigate, edit and debug your ZF2-based application in an effective manner.
https://netbeans.org/
http://www.oracle.com/technetwork/java/javase/downloads/index.html

Appendix A. Configuring Web Development Environment

These installation instructions do not work for me. What do I do?


Please contact the author using the dedicated Google Group forum. The author
appreciates your feedback and will be happy to impove the installation instructions.

https://groups.google.com/forum/#!forum/using-zend-framework-2-book

367

Appendix B. Introduction to PHP


Development in NetBeans IDE
In this book, we use NetBeans IDE for developing Zend Framework 2 based applications. In
Appendix A we have installed NetBeans. Here we will provide some useful tips on using
NetBeans for PHP programming. We will learn how to launch and interactively debug a ZF2based web site.
What if I want to use another IDE (not NetBeans) for developing my applications?
Well, you can use any IDE you want. The problem is that it is impossible to cover
all IDEs for PHP development in this book. The author only provides instructions
for NetBeans IDE. It would be easier for a beginner to use NetBeans IDE. Advanced
developers may use an IDE of their choices.

Run Configuration
To be able to run and debug a web site, you first need to edit the sites properties. To do that,
in NetBeans Projects pane, right-click on the projects name, and from context menu select
Properties item. The projects Properties dialog will appear (shown in figure B.1).

Appendix B. Introduction to PHP Development in NetBeans IDE

369

Figure B.1. Properties | Sources

In the left pane of the dialog that appears, click the Sources node. In the right pane, edit the Web
Root field to point to your web sites APP_DIR/public directory. You can do this by clicking the
Browse button to the right of the field. Then, in the dialog, click on the public directory and then
click Select Folder button (shown in figure B.2).

Figure B.2. Browse Folders Dialog

Next, click the Run Configuration node in the left pane. The right pane should display the run
settings for your site (figure B.3).

Appendix B. Introduction to PHP Development in NetBeans IDE

370

Figure B.3. Properties | Run Configuration

In the right pane, you can see that the current configuration is default. As an option, you can
create several run configurations.
Edit the fields as follows:
In the Run As field, select Local Website (running on local web server).
In the Project URL field, enter http://localhost. If you configured your virtual host to listen
on different port (say, on port 8080), enter the port number like this http://localhost:8080.
Keep the Index File field empty, because Apaches mod_rewrite module will mask our
actual index.php file.
In the Arguments field, you can specify which GET parameters to pass to your site through
the URL string. Typically, you keep this field empty.
Finally, click the OK button to close the Properties dialog.

Running the Web Site


Running the web site means opening it in your default web browser. To launch the site, press
the Run button on the Run Toolbar (figure B.4). Alternatively, you can press F6 button on your
keyboard.

Appendix B. Introduction to PHP Development in NetBeans IDE

371

Figure B.4. Run Toolbar

If everything is OK with your run configuration, the default web browser will be launched, and
in the browser window, you will be able to see the Home page of the web site.
The same effect would have typing http://localhost/ in your browser, but NetBeans run toolbar
allows you to do that in a single click.

Site Debugging in NetBeans


The conventional debugging technique in PHP is putting the var_dump() function in the code
block that you want to examine:
var_dump($var);
exit;

The lines above would print the value of the $var variable to the browsers output and then
stop program execution. While this can be used for debugging even a complex site, it makes the
debugging a pain, because you have to type variable dump commands in your PHP source file,
then refresh your web page in the browser to see the output, then edit the source file again, until
you determine the reason of the problem.
In contrast, when you debug your web site in NetBeans IDE, the PHP interpreter pauses program
execution flow at every line where you set a breakpoint. This makes it possible to conveniently
retrieve information about the current state of the program, like the values of the local variables
and the call stack. You see the debugging information in NetBeans window in graphical form.
To be able to debug the site, you need to have the XDebug extension installed. If you
havent installed it yet, please refer to Appendix A for additional information on how
to install it.

To start the debugging session, in NetBeans window, click the Debug button on the Run Toolbar
(figure B.4). Alternatively, you can press the CTRL+F5 key combination on your keyboard.
If everything is OK, you should be able to see the current program counter on the first code line
of the index.php file (shown in figure B.5):

Appendix B. Introduction to PHP Development in NetBeans IDE

372

Figure B.5. Debugging Session

While the program is in paused state, your web browsers window is frozen, because the browser
is waiting for data from the web server. Once you continue program execution, the browser
receives the data, and displays the web page.

Debug Toolbar
You can resume/suspend program execution with the Debug Toolbar (see figure B.6):

Figure B.6. Debug Toolbar

The Finish Debugger Session of the toolbar allows to stop the debugger. Press this button when
youre done with debugging the program. The same effect would have pressing the SHIFT+F5
key combination.
Clicking the Continue button (or pressing F5 key) continues the program execution until the next
breakpoint, or until the end of the program, if there are no more breakpoints.

Appendix B. Introduction to PHP Development in NetBeans IDE

373

The Step Over toolbar button (or pressing F8 key) moves the current program counter to the next
line of the program.
The Step Into toolbar button (or pressing F7 key) moves the current program counter to the next
line of the program, and if it is the function entry point, enters the function body. Use this when
you want to investigate your code in-depth.
The Step Out toolbar button (CTRL+F7) allows to continue program execution until returning
from the current function.
And Run to Cursor (F4) allows to continue program execution until the line of code where you
place the cursor. This may be convenient if you want to skip some code block and pause at a
certain line of your program.

Breakpoints
Typically, you set one or several breakpoints to the lines which you want to debug in step-bystep mode. To set a breakpoint, put your mouse to the left of the line of code where you want the
breakpoint to appear and click on the line number. Alternatively, you can put the cursor caret to
the line where you want to set a breakpoint and press CTRL+F8 key combination.
When you set the breakpoint, the line is marked with red color and a small red rectangle appears
to the left of it (shown in figure B.7):

Figure B.7. Setting a breakpoint

Appendix B. Introduction to PHP Development in NetBeans IDE

374

Be careful not to set a breakpoint on an empty line or on a comment line. Such a


breakpoint will be ignored by XDebug, and it will be marked by the broken square
(see figure B.8 for example):

Figure B.8. Inactive breakpoint

You can travel between breakpoints with the F5 key press. This button continues program
execution until it encounters the next breakpoint. Once the program flow comes to the
breakpoint, the PHP interpreted becomes paused, and you can review the state of the program.
You can see the complete list of breakpoints you have set in the Breakpoints window (see figure
B.9). The Breakpoints window is located in the bottom part of NetBeans window. In this window
you can add new breakpoints or unset breakpoints that have already been set.

Figure B.9. Breakpoints window

Watching Variables
When the PHP interpreter is paused, you can conveniently watch the values of PHP variables.
A simple way to browse a variable is just positioning the mouse cursor over the variable name
inside of the code and waiting for a second. If the variable value can be evaluated, it will be
displayed inside of a small pop up window.
Another way to watch variables is through the Variables window (shown in figure B.10), which
is displayed in the bottom part of NetBeans window. The Variables window has three columns:
Name, Type and Value.

Appendix B. Introduction to PHP Development in NetBeans IDE

375

Figure B.10. Variables window

Mainly, you will be faced with three kinds of variables: super globals, locals and $this:
Super global variables are special PHP variables like $_GET, $_POST, $_SERVER, $_COOKIES
and so on. They typically contain server information and parameters passed by the web
browser as part of HTTP request.
Local variables are variables living in the scope of the current function (or class method).
For example, in the Hello World application, if you place a breakpoint inside of the
IndexController::aboutAction(), the variable $zendFrameworkVer will be a local variable.
$this variable points to the current class instance, if the current code is being executed in
context of a PHP class.
Some variables can be expanded (to expand a variable, you need to click on a triangle icon
to the left of variables name). For example, by clicking and expanding $this variable, you can
watch all fields of the class instance. If you expand an array variable, you will be able to watch
array items.
Using the Variables window it is possible not only to watch variables value, but also to change
the value on the fly. To do that, place your mouse cursor over the value column and click over
it. The edit box appears, where you can set the new value of the variable.

Call Stack
The call stack displays the list of nested functions whose code is being executed at the moment
(shown in the figure B.11). Each line of the call stack (also called a stack frame) contains the full
name of the class, the name of the method within the class and line number. Moving down the
stack, you can better understand the current execution state of the program.

Appendix B. Introduction to PHP Development in NetBeans IDE

376

Figure B.11. Call Stack window

For example, in figure B.11, you can see that currently the IndexController::aboutAction() is
being executed, and this method was in turn called by the AbstractActionController::onDispatch()
method, and so on. We can walk the call stack until we reach the index.php file, which is the top
of the stack. You can also click a stack frame to see the place of the code that is currently being
executed.

Debugging Options
NetBeans allows you to configure some aspects of the debuggers behavior. To open the Options
dialog, select menu Tools->Options. In the dialog that appears, click the PHP tab, and in that tab,
select Debugging subtab (figure B.12).

Appendix B. Introduction to PHP Development in NetBeans IDE

377

Figure B.12. PHP Debugging Options

You typically do not change most of these options, you just need to have an idea of what they
do. These are the following debugging options:
The Debugger Port and Session ID parameters define how NetBeans connects to XDebug.
By default, the port number is 9000. The port number should be the same as the debugger
port you set in php.ini file when installing XDebug. The session name is by default
netbeans-xdebug. You typically do not change this value.
The Stop at First Line parameter makes the debugger to stop at the first line of your
index.php file, instead of stopping at the first breakpoint. This may be annoying, so you
may want to uncheck this option.
The Watches and Balloon Evaluation option group is disabled by default, because these
may cause XDebug fault. You can enable these options only when you know what you are
doing.
The Maximum Depth of Structures parameter sets whether nested structures (like
nested arrays, objects in objects, etc.) will be visible or not. By default, the depth is
set to 3.
The Maximum Number of Children option defines how many array items to display
in Variables window. If you set this to, say 30, you will see only the first 30 items
even when the array has more than 30 items.
The Show Requested URLs option, when enabled, displays the URL which is currently
being processed. It prints the URL to an Output window.

Appendix B. Introduction to PHP Development in NetBeans IDE

378

The Debugger Console option allows to see the output of PHP scripts being debugged. The
output is shown in the Output window. If you plan to use this feature, it is recommended to
add output_buffering = Off parameter in [xdebug] section of your php.ini file, otherwise
the output may appear with delays.

Profiling
When your site is ready and working, you are typically interested in making it as fast and
performing as possible. XDebug provides you with an ability to profile your web site. Profiling
means determining which class methods (or functions) spend what time to execute. This allows
you to determine the bottle neck places in your code and address the performance issues.
For each HTTP request, the XDebug extension measures the amount of time a function
executes, and writes the profiling information to a file. Typically, the profiling info files are
placed into the system temporary directory (in Linux, to /tmp directory) and have names like
xdebug.out.<timestamp>, where the <timestamp> placeholder is the timestamp of the HTTP
request. All you have to do is to open a profiling file and analyze it.
To enable XDebug profiler, you should set the following XDebug configuration
parameter in your php.ini file:
xdebug.profiler_enable = 1

Unfortunately, NetBeans for PHP does not have an embedded tool for visualizing the profiling
results. Thats why you need to install a third-party visualizer tool. Below, we will provide
instructions on how to install a simple web-based tool named Webgrind. Webgrind can work
on any platform, because this tool itself is written in PHP.
Webgrinds installation is very straightforward.
First, you need to download webgrind from its project page and unpack it to some folder. In
Linux, you can do this with the following commands:
cd
wget https://webgrind.googlecode.com/files/webgrind-release-1.0.zip
unzip webgrind-release-1.0.zip

The commands above will change your working directory to your home directory, then will
download the Webgrind archive from the Internet, and then unpack the archive.
Next, you need to tell the Apache web server where to find Webgrind files. This means you need
to configure a separate virtual host. We have already learned about virtual hosts in Appendix A.
Do not forget to restart Apache web server after you have configured the virtual host.
Finally, open Webgrind in your browser by navigating to the URL of your Webgrind install. For
example, if you configured the virtual host to listen on port 8080, enter http://localhost:8080
https://code.google.com/p/webgrind/

Appendix B. Introduction to PHP Development in NetBeans IDE

379

in your browsers navigation bar and press Enter. The Webgrind web page should appear (see
figure B.13):

Figure B.13. Webgrind Output Page

At the top of the Webgrind page, you can select the percentage of the heaviest function calls
to show (figure B.14). By default, it is set to 90%. Setting this to a lower percentage will hide the
functions called less often.

Figure B.14. Webgrind Select

The drop-down list to the right of percent field allows to select the profiling data file to analyze.
By default, it is set to Auto (newest), which forces Webgrind to use the file with the most recent
timestamp. You may need to select another file, for example, if your web pages use asynchronous
AJAX requests.
The right-most drop-down list allows to set the units which should be used for measuring the
data. Possible options are: percent (default), milliseconds and microseconds.

Appendix B. Introduction to PHP Development in NetBeans IDE

380

When you have selected the percentage, file name and units, click the Update button to let
Webgrind to visualize the data for you (the calculation may take a few seconds). As the
calculation finishes, you should be able to see the table of function calls, sorted in descending
order by function weight. The heaviest functions will be displayed at the top.
The table has the following columns:
The first column (Function), displays the class name followed by method name (in case of
a method call) or function name (in case of a regular function).
The second column contains the paragraph icons, which can be clicked to open the
corresponding PHP source file that function is defined in the web browser.
Invocation Count column displays the number of times the function was called.
Total Self Cost column shows the total time it took to execute the built-in PHP code in the
function (excluding the time spent on executing other non-standard functions).
Total Inclusive Cost column contains the total execution time for the function, including
built-in PHP code and any other user functions called.
Clicking a column header allows to sort data in ascending or descending order.
You can click the triangle icon next to a function name to expand a list of function invocations.
This list allows you to see who called this function and what the amount of time spent is, and
contains the following columns:
Calls is the parent functions or class methods invoking this (child) function;
Total Call Cost is the total time executing this function, when called from the parent
function;
Count - number of times the parent calls the child function.
The coloured bar at the top of the page displays the contribution of different function types:

Blue denotes PHP internal (built-in) functions;


Lavender is time taken to include (or require) PHP files;
Green shows the contribution of your own class methods;
Orange denotes time taken on traditional procedural functions (functions that are not
part of a PHP classes).

Please note that the profiler creates a new data file in your /tmp directory for each
HTTP request to your web site. This may cause disk space exhaustion, which can
be fixed only by rebooting your system. So, when youve finished profiling your
application, it is recommended to disable the profiling by editing the php.ini file,
commenting the xdebug.profiler_enable parameter as follows, and then restarting
the Apache web server.
;xdebug.profiler_enable = 0

Appendix B. Introduction to PHP Development in NetBeans IDE

381

Summary
In this appendix, weve learned how to use NetBeans IDE to run the web site and debug it
in interactive step-by-step mode. To be able to run a web site, you first need to edit the sites
properties (run configuration).
To debug the site, you need to have the XDebug PHP extension installed. When you debug your
web site in NetBeans, the PHP engine pauses program execution at every line where you set a
breakpoint. You see the debugging information (like local variables and call stack) in NetBeans
window in graphical form.
Along with debugging, XDebug extension also provides the ability to profile web sites. With
profiling, you see how much time was spent for execution of a certain function or class method.
This allows you to determine the bottle necks and performance issues.

Appendix C. Introduction to Twitter


Bootstrap
Twitter Bootstrap (shortly, Bootstrap) is a popular CSS framework allowing to make your web
site professionally looking and visually appealing, even if you dont have advanced designer
skills. In this appendix, you can find some introductive information about Bootstrap and its
usage examples.

Overview of Bootstrap Files


The source code of Bootstrap frameworks components is spread across many CSS files. It is
known that downloading multiple small files is typically slower than downloading a single
large file. For this reason, Bootstrap CSS stylesheets are concatenated with the special tool
and distributed in a form of a single file named bootstrap.css.
However, this bootstrap.css file has a disadvantage: it contains many characters (white space
characters, new line characters, comments, etc.) unneeded for code execution, wasting network
bandwidth when downloading the file, thus increasing page load time. To fix this problem, the
minification is used.
The minification is the process of removing all unnecessary characters from the source code
without changing its functionality. The minified Bootstrap file is called bootstrap.min.css.
It is generally recommended to use the minified file, especially in production environment, because it reduces the page load time. However, if you plan to dig into Bootstrap
code to understand how it works, you better use the usual (non-minified) file, or even
download the original source files (not concatenated ones).

Lets look in more details at the files stored inside the APP_DIR/public directory and its
subdirectories (figure C.1).
The css directory contains the CSS stylesheets:
The bootstrap.css and bootstrap.min.css files are the usual and minified versions of
Bootstrap, respectively.
The bootstrap-theme.css is the optional Bootstrap theme file for a visually enhanced
experience. The bootstrap-theme.min.css is its minified version.
The style.css file is the stylesheet that can be used and extended by you to define your own
CSS rules which will be applied on top of Bootstrap rules. This way you can customize the
appearance of your web application.

Appendix C. Introduction to Twitter Bootstrap

383

The fonts directory contains several files (e.g. glyphicons-halflings-regular.svg) needed by


Bootstrap for rendering icons. These icons (also called Glyphicons) can be used to enhance the
appearance of the buttons and dropdown menus.

Figure C.1. Structure of the APP_DIR/public directory

The APP_DIR/public/js subdirectory contains JavaScript extensions of the Bootstrap framework.


They are implemented as jQuery plugins:
The bootstrap.js is the file containing the JavaScript code of the Bootstrap extensions. The
bootstrap.min.js file is its minified version.
Because Bootstrap extensions are implemented as jQuery plugins, they require the latest
version of jQuery library to be present. Thus, the js directory includes the jQuery library
file jquery.min.js. You may also notice the jquery-1.10.2.min.map file, which is the MAP
file that can be used for debugging.

The html5shiv.js and respond.min.js files are actually not part of either Bootstrap or
jQuery. They are used for compatibility with older versions of Internet Explorer web
browser. The first one enables the use of HTML5 elements and provides basic HTML5
styling, and the latter one enables the responsive web designs.

After the concatenation and minification, the JavaScript code is difficult to read and debug. A MAP file (source map) allows to restore the
minified file back to its usual state.
https://github.com/aFarkas/html5shiv
https://github.com/scottjehl/Respond

Appendix C. Introduction to Twitter Bootstrap

384

Grid System
In most web sites, content is required to be organized in a table-like structure having rows and
columns. In figure C.2, you can see an example layout of a typical web site: it has the header
block with a logo, the sidebar at the left, page content area in the middle, the ads bar at the right,
and the footer at the bottom of the page. These blocks are arranged in a grid, although grid cells
have unequal width (some cells can span several columns).

Figure C.2. A typical site layout

Bootstrap provides a simple layout grid system to make it easy to arrange content on your pages
in rows and columns.
Each row consists of up to 12 columns (figure C.3). Column width is flexible and depends on
the width of the grid container element. Column height may vary depending on the height of
the content of the cell. The space between columns is 30 pixels (15 pixels padding at both sides
of the column).

Figure C.3. Bootstrap grid system

Columns can be spanned, so a single cell takes the space of several columns. For example, in
figure C.3, the upper grid row consists of 12 columns, and each cell spans a single column. In the
bottom row, the first cell spans 2 columns, the second and the third ones span 4 columns each,
and the fourth cell spans 2 columns (in total we have 12 columns).
Why does Bootstraps grid consist of only 12 columns?
Probably because 12 columns are enough for most web sites. If you have more finegrained grid with lots of columns, it would be more difficult to compute column spans
without the calculator. Fortunately, Bootstrap allows for customizing the count of
columns per row, so you can have as many columns as you wish.

You are not required to put exactly 12 columns in a row, there may be fewer columns. If you have fewer columns, the space to the right of
the last column will be empty.

Appendix C. Introduction to Twitter Bootstrap

385

Defining the Grid


To arrange elements in a grid on your web page, you start from defining the container by adding
a <div> element having the container CSS class. To add a new row to the grid, use a <div>
element having the row CSS class, as shown in the example below:
<div class="container">
<div class="row">
...
</div>
</div>

To add columns, you use <div> elements with CSS class names varying from col-md-1 to
col-md-12. The number in the class name specifies how many columns each grid cell will span:
<div class="container">
<div class="row">
<div class="col-md-1">Cell 1</div>
<div class="col-md-5">Cell 2</div>
<div class="col-md-6">Cell 3</div>
</div>
</div>

In the example above, we have three cells. The first cell has a width of 1 (it uses the col-md-1
class), the second cell spans 5 grid columns (class col-md-5) and the third cell spans 6 columns
(class col-md-6).
As another example, lets define the layout that we saw in figure C.4. The layout has the header
(logo spans 3 columns), the main content area (spans 7 columns), the sidebar (spans 3 columns),
the advertisements bar (2 columns) and the footer. To produce this layout, we can use the
following HTML code:
<div class="container">
<!-- Header -->
<div class="row">
<div class="col-md-3">Logo</div>
<div class="col-md-9"></div>
</div>
<!-- Body-->
<div class="row">
<div class="col-md-3">Sidebar</div>
<div class="col-md-7">Page Content</div>
<div class="col-md-2">Ads</div>
</div>
<!-- Footer -->

Appendix C. Introduction to Twitter Bootstrap

386

<div class="row">
<div class="col-md-12">Page Footer</div>
</div>
</div>

Offsetting Columns
In real web pages, sometimes the grid needs to contain empty holes. You can define such
holes by offsetting cells to the right with the help of CSS classes named from col-md-offset-1
to col-md-offset-12. The number in the class name specifies how many columns should be
skipped.
For example, look at figure C.4:

Figure C.4. Column offsets

The grid above has three cells, the latter two cells are offsetted to the right, making empty holes.
To define the grid like in figure C.4, you can use the following HTML code:
1
2
3
4
5
6
7

<div class="container">
<div class="row">
<div class="col-md-2">Cell 1</div>
<div class="col-md-4 col-md-offset-2">Cell 2</div>
<div class="col-md-2 col-md-offset-2">Cell 3</div>
</div>
</div>

Nesting Grids
You can create complex page layouts by nesting grids (for example, look at figure C.5). To nest
your content, you add a new <div> element containing row class, and set of col-md-* columns
within an existing col-md-* column.

Figure C.5. Nested grid

To produce the grid as shown in figure C.5, you can use the following HTML code:

Appendix C. Introduction to Twitter Bootstrap

387

<div class="container">
<div class="row">
<div class="col-md-2">Cell 1</div>
<div class="col-md-8">
<!-- Nested grid -->
<div class="row">
<div class="col-md-4">Cell 21</div>
<div class="col-md-4">Cell 22</div>
</div>
<div class="row">
<div class="col-md-4">Cell 23</div>
<div class="col-md-4">Cell 24</div>
</div>
</div>
<div class="col-md-2">Cell 3</div>
</div>
</div>

In the example above, we defined the grid consisting of three cells (denoted by gray color): the
first cell spanning 2 columns, the second cell spanning 8 columns and the third cell spanning
2 columns. Then we put the nested grid rows inside of the second cell. Because the parent cell
spans 8 columns, the child grid consists of 8 columns, too.

Mobile First Concept


Twitter Bootstrap v3.0 is designed to support different devices varying from wide displays
to tablets and mobile phones. By this reason, the layout grid is adapted to different screen
resolutions.
This is also called the responsiveness, or the mobile first concept. Bootstrap v3.0 is
mobile-first, which means your web site will be viewable and usable on any-sized
screen. However, this does not free you of painstaking preparation and planning the
layout.

This adaptation is performed in two ways. The first way is that the column width within the grid
is flexible. For example, if you increase the size of the browser window, the grid will be scaled
accordingly to fill the whole space.
But what will happen if your web page is too wide for the display? To see the hidden part, the site
visitor will need to scroll it to the right. For mobile phones and other low-resolution devices this
is not a good approach. Instead, it would be better for the grid to become stacked below some
screen width. When the grid is stacked, its rows are transformed, making cells to be positioned
one below another (see figure C.6 for example).
To better control when the grid becomes stacked, Bootstrap provides you with additional CSS
classes: col-xs-1 to col-xs-12 (the xs abbreviation means extra-small devices, or phones),

388

Appendix C. Introduction to Twitter Bootstrap

col-sm-1 to col-sm-12 (sm stands for small devices, or tablets), and col-lg-1 to col-lg-12
(large devices, or wide displays). These classes can be used together with the col-md-1
col-md-12 classes, that we already used (the md abbreviation means medium devices, or

desktops).
For example, col-md-* classes define the grid which will become stacked when the screen is
below 992 pixels wide, and horizontal for wider screens. The col-sm-* can be used to make the
grid stacked below 768 pixel screen width, and horizontal above this point. The col-xs-* class
makes the grid always horizontal, not depending on the screen width.

Figure C.6. Grid adapts to screen size

Table C.1 provides the summary of available grid classes and their breakdown page width.
Table C.1. CSS classes for defining layout grid

Class name

Breakdown width

col-xs-*
col-sm-*
col-md-*
col-lg-*

<768px
>=768px
>=992px
>=1200px
Bootstraps grid system greatly simplifies the positioning of elements on a web page.
However, using the grid system is not mandatory. For example, sometimes you may
need a much more complex layout, and the simple grid system will be insufficient. In
such a case, you can create and use your custom layout by using <table> or <div>
HTML elements.

Bootstraps Interface Components


In this section, we will give a summary on useful interface components provided by Twitter
Bootstrap. We plan to use some of these components in our further examples, and this section
should give an idea of how you can use these widgets in your own web sites.

389

Appendix C. Introduction to Twitter Bootstrap

Navigation Bar
Navigation bar is usually positioned on top of your web site and contains the links to main pages,
like Home, Download, Support, About, etc. Twitter Bootstrap provides a nice visual style for the
navbar (see figure C.7 for example):

Figure C.7. Navbar

As you can see from the figure above, a navbar typically has the header (brand name of your
site can be placed here) and the links to main pages. To put a navbar on your page, you use the
following HTML code:
1
2
3
4
5
6
7
8
9
10
11

<nav class="navbar navbar-default" role="navigation">


<div class="navbar-header">
<a class="navbar-brand" href="#">Hello World</a>
</div>
<ul class="nav navbar-nav">
<li><a href="#">Home</a></li>
<li><a href="#">Download</a></li>
<li><a href="#">Support</a></li>
<li><a href="#">About</a></li>
</ul>
</nav>

In line 1 above, we used the <nav> element, which contains all the navigation bar information.
The associated CSS class navbar is defined by Bootstrap and provides the base navigation bars
appearance. The navbar-default CSS class specifies the default theme for the navigation bar.
The optional role attribute is an HTML attribute allowing to annotate the page elements with
machine-extractable semantic information about the purpose of an element. In this example, the
attribute tells that the <nav> element is used for navigation.
In lines 2-4, we define the navbar header area, which contains the Hello World hyperlink.
The brand hyperlink typically points to the main page of your site. The hyperlink has the
navbar-brand class that visually enhances the text.
In lines 5-10, we specify the navigation links for the Home, Download, Support and About pages.
These links are organized inside an <ul> unordered list element. The element has CSS classes
nav and navbar-nav that place list items in line and provide the hover item state.
Dropdown Menu
With Bootstrap navigation bar, it is possible to use the dropdown menu as a navigation item.
For example, if the Support section of your site can be subdivided into Documentation and Help
pages, these can be implemented as a dropdown menu (see figure C.8).
http://www.w3.org/TR/xhtml-role/

Appendix C. Introduction to Twitter Bootstrap

390

Figure C.8. Navbar with dropdown menu

You define the dropdown menu by replacing the Support list item from the previous example in
the following way:
1
2
3
4
5
6
7
8
9

<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Support <b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="#">Documentation</a></li>
<li><a href="#">Help</a></li>
</ul>
</li>

In the code above, we use the <li> element with CSS class dropdown that indicates the dropdown
menu (line 1). In lines 2-4, the <a> element defines the hyperlink to show when the menu is hidden
(the Support text is shown followed by the triangle caret).
When the site user clicks the hyperlink, the dropdown menu (lines 5-8) appears. The <ul>
unordered list element with class dropdown-menu defines its visual appearance. The dropdown
menu contains two items: the Documentation and Help hyperlinks.
Collapsible Navbar
As with the grid system, the navbar component supports different types of screen resolutions.
On low-resolution devices, the navbar can be collapsed, as shown in figure C.9.

Figure C.9. Collapsed navbar

As you can see, in the collapsed mode, only the navbar header is displayed, and the three
horizontal bars at the right denote the Toggle button. Clicking the button would expand the
hidden navbar items.
You define the collapsible navigation bar as shown in the example below:

Appendix C. Introduction to Twitter Bootstrap


1
2
3
4
5
6
7
8
9
10
11
12

391

<nav class="navbar navbar-default" role="navigation">


<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse"
data-target=".navbar-ex1-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Hello World</a>
</div>

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse navbar-ex1-collapse">
<ul class="nav navbar-nav">
<li><a href="#">Home</a></li>
<li><a href="#">Download</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Support <b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="#">Documentation</a></li>
<li><a href="#">Help</a></li>
</ul>
</li>
<li><a href="#">About</a></li>
</ul>
</div><!-- /.navbar-collapse -->
</nav>

Above in lines 3-12, we define the navbar header which will be displayed independently on screen
resolution. The header contains the Toggle button with three horizontal bars and description text
Toggle navigation.
The collapsible part of the menu can be seen in lines 15-30. In this area, we put our navigation
links and the dropdown menu items.
Inverse Navbar Style
The navigation bar can be displayed using two standard themes: the default theme (we saw it
above), and the inverse theme. The inverse theme makes the navbar elements be displayed in dark
colors (figure C.10). You probably saw such an inverse navbar in the Zend Skeleton Application
demo.

Appendix C. Introduction to Twitter Bootstrap

392

Figure C.10. Navbar inverse style

The inverse theme is defined by simply replacing the navbar-default class of the <nav> element
by the navbar-inverse class:
<nav class="navbar navbar-inverse" role="navigation">
...
</nav>

Breadcrumbs
Breadcrumbs is a useful interface component which can be used together with the navbar to give
the site visitor an idea of his current location within the site (figure C.11).

Figure C.11. Breadcrumbs

In the figure above, we have an example breadcrumbs for the documentation system of our site.
Because the documentation pages can have deep nesting level, the breadcrumbs tell the user
which page he is visiting right now so the user will not get lost and will be able to return to the
page he visited previously, and to the upper-level pages.
To define the breadcrumbs, you use the ordered list <ol> element with the breadcrumb CSS class
(see an example below):
<ol class="breadcrumb">
<li><a href="#">Home</a></li>
<li><a href="#">Support</a></li>
<li class="active">Documentation</li>
</ol>

Pagination
The pagination component is useful when you have a long list of items for display. Such a long
list, if displayed on a single page, would require the user to scroll the page down several times to
see the bottom of the list. To improve user experience, you would break the output into pages,
and use the pagination component for navigation between the pages (figure C.12):

Figure C.12. Pagination

To define the pagination like in figure above, use the following HTML code:

393

Appendix C. Introduction to Twitter Bootstrap

<ul class="pagination">
<li><a href="#">&laquo; Newest</a></li>
<li><a href="#">&lt; Newer</a></li>
<li><a href="#">1</a></li>
<li><a href="#">2</a></li>
<li><a href="#">3</a></li>
<li><a href="#">4</a></li>
<li><a href="#">5</a></li>
<li><a href="#">Older &gt;</a></li>
<li><a href="#">Oldest &raquo</a></li>
</ul>

Buttons & Glyphicons


Twitter Bootstrap provides a nice visual style for button elements (figure C.13).

Figure C.13. Buttons

To create the buttons like in the figure above, use the following HTML code:
<p>
<button type="button" class="btn btn-primary">Save</button>
<button type="button" class="btn btn-default">Cancel</button>
</p>

In the code above, we use the btn CSS class to assign the button its visual style. Additionally, we
use the btn-primary class for the Save button (which is typically the primary button on a form),
or the btn-default class for a usual non-primary button Cancel.
To better express the meaning of a button, Bootstrap provides you with several additional
button classes: btn-success (for buttons applying some change on the page), btn-info (for
informational buttons), btn-warning (for buttons that may have an undesired effect), and
btn-danger (for buttons that may lead to irreversible consequences). For an example of using
these button styles, look at the code below:
<p>
<button
<button
<button
<button
<button
<button
</p>

type="button"
type="button"
type="button"
type="button"
type="button"
type="button"

class="btn
class="btn
class="btn
class="btn
class="btn
class="btn

btn-default">Default</button>
btn-primary">Primary</button>
btn-success">Success</button>
btn-info">Info</button>
btn-warning">Warning</button>
btn-danger">Danger</button>

Appendix C. Introduction to Twitter Bootstrap

394

Figure C.14 shows the resulting button appearance:

Figure C.14. Button styles

Bootstrap includes 180 icons (called Glyphicons) that you can use together with your buttons,
dropdown menus, navigation links, etc. To add an icon on a button, you can use the code like
the one below:
<p>
<button type="button" class="btn btn-default">
<span class="glyphicon glyphicon-plus"></span> Create
</button>
<button type="button" class="btn btn-default">
<span class="glyphicon glyphicon-pencil"></span> Edit
</button>
<button type="button" class="btn btn-default">
<span class="glyphicon glyphicon-remove"></span> Delete
</button>
</p>

In the code above, we defined a simple toolbar containing three buttons: Create, Edit and Delete.
We placed an icon on each button with the help of <span> element. The <span> element should
have two classes: the glyphicon class is common for all icons; the second class represents the
icon name. In the example above, we used glyphicon-plus class for the Create button, the
glyphicon-pencil for Edit button, and glyphicon-remove for Delete button. The result of our
work is presented in figure C.15.

Figure C.15. Buttons with icons

You can vary button sizes by specifying the btn-lg class for large buttons, btn-sm for small
buttons, or btn-xs class for extra-small buttons. For example, in figure C.16, a large Download
button is presented.

Figure C.16. Large button

To define such a button, you can use the following HTML code:

Appendix C. Introduction to Twitter Bootstrap

395

<button type="button" class="btn btn-success btn-lg">


<span class="glyphicon glyphicon-download"></span> Download
</button>

Customizing Bootstrap
To finish the introduction to Twitter Bootstrap, we will describe about how to modify some
aspects of Bootstrap framework. You can customize the Bootstrap look and feel using the
Customize page of the Bootstrap web site (figure C.17).

Figure C.17. Bootstraps Customize Page

On the Customize page you can choose which Bootstrap source files to include into the
concatenated resulting file bootstrap.css. If you dont need some functionality, you can exclude
http://getbootstrap.com/customize/

Appendix C. Introduction to Twitter Bootstrap

396

it from the resulting file, thus reducing the network traffic and page load time. You can also
remove some unused JavaScript code components from the resulting bootstrap.js file.
Additionally, you can choose different CSS parameters like background color, base text color and
font family, and so on. There are more than a hundred customizable parameters available.
CSS customization is possible, because Bootstrap source files are stored in LESS format, which allows to define variable parameters (like @bodyBackground or @textColor).
Once the parameters are defined, the LESS files are compiled into usual CSS files,
minified and made available for downloading.

When you have finished with tuning parameters, you can scroll the Customize page down
and press the Compile and Download button. As a result, the bootstrap.zip archive will be
downloaded, which contains all the customized Bootstrap files (both usual and minified CSS
and JS files and glyphicons fonts).

Summary
Twitter Bootstrap is a CSS framework developed to make designing your web pages easier. It
provides the default nice-looking style for typography, tables, forms, buttons, images and so on,
so you can create a professionally looking page in a minute.
The grid system provided by the Bootstrap allows to arrange elements on your web page in a
grid with rows and columns. The grid adapts to different screen resolutions, making your page
equally well-readable on mobile phones, tablets, desktops and wide screens.
Twitter Bootstrap also provides useful web interface components like dropdown menus, navigation bars, breadcrumbs, etc. These components are made interactive by the JavaScript extensions
bundled with the framework.
Bootstrap is shipped with Zend Skeleton Application, so you can start using it out of the box
or, alternatively, you can download the newest version of Bootstrap from the projects page and
customize it as you wish.
LESS is a dynamic stylesheet language extending standard CSS with features like variables, mixins (embedding all the properties of a CSS
class into another CSS class), code block nesting, arithmetic operations, and functions.

About the Author


Oleg Krivtsov is a software developer currently living in Tomsk,
Russia. He received a PhD degree in Computer Science from Tomsk
Polytechnic University in 2010. Oleg has been professionally developing C/C++ and PHP software since 2005. He also taught Digital
Signal Processing in the university. Oleg likes contributing to opensource and writing programming articles for popular web resources,
like CodeProject. This writing passion has also inspired him to create
this book about Zend Framework 2. He also writes posts to his
personal blog on a regular basis. You can contact Oleg through
his E-mail address olegkrivtsov@gmail.com.

http://olegkrivtcov.wordpress.com/

Você também pode gostar