SlideShare a Scribd company logo
Zend Framework 2 Documentation
Release 2.2.5

Zend Technologies Ltd.

October 31, 2013
Zend framework tutorial
Contents

1

Overview

1

2

Installation

3

3

Getting Started with Zend Framework 2
3.1 Some assumptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2 The tutorial application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

5
5
5

4

Getting started: A skeleton application
4.1 Using the Apache Web Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.2 Error reporting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7
8
9

5

Modules
5.1 Setting up the Album module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2 Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.3 Informing the application about our new module . . . . . . . . . . . . . . . . . . . . . . . . . . . .

11
11
13
13

6

Routing and controllers

15

7

Create the controller
7.1 Initialise the view scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

17
18

8

Database and models
8.1 The database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.2 The model files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.3 Using ServiceManager to configure the table gateway and inject into the AlbumTable .
8.4 Back to the controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.5 Listing albums . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

19
19
19
21
23
23

9

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

Styling and Translations

10 Forms and actions
10.1 Adding new albums . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10.2 Editing an album . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10.3 Deleting an album . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

27
29
29
34
36
i
10.4 Ensuring that the home page displays the list of albums . . . . . . . . . . . . . . . . . . . . . . . . .
11 Conclusion

38
39

12 Zend Framework Tool (ZFTool)
12.1 Installation using Composer . . . . . . .
12.2 Manual installation . . . . . . . . . . . .
12.3 Without installation, using the PHAR file
12.4 Usage . . . . . . . . . . . . . . . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

41
41
41
41
42

13 Learning Dependency Injection
13.1 Very brief introduction to Di. . . . . . . . . . . . . . .
13.2 Simplest usage case (2 classes, one consumes the other)
13.3 Simplest Usage Case Without Type-hints . . . . . . . .
13.4 Simplest usage case with Compiled Definition . . . . .
13.5 Creating a precompiled definition for others to use . . .
13.6 Using Multiple Definitions From Multiple Sources . . .
13.7 Generating Service Locators . . . . . . . . . . . . . . .

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

45
45
45
47
48
50
50
51

14 Unit Testing a Zend Framework 2 application
14.1 Setting up the tests directory . . . . . . . . .
14.2 Bootstrapping your tests . . . . . . . . . . .
14.3 Your first controller test . . . . . . . . . . .
14.4 A failing test case . . . . . . . . . . . . . .
14.5 Configuring the service manager for the tests
14.6 Testing actions with POST . . . . . . . . . .
14.7 Testing model entities . . . . . . . . . . . .
14.8 Testing model tables . . . . . . . . . . . . .
14.9 Conclusion . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

55
55
56
58
59
60
61
62
64
68

15 Using the EventManager
15.1 Terminology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
15.2 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
15.3 Shared managers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

69
69
69
71

16 Wildcards

73

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

17 Listener aggregates
17.1 Introspecting results . . . . . . . . . . . . . . . . . . . .
17.2 Short-ciruiting listener execution . . . . . . . . . . . . .
17.3 Keeping it in order . . . . . . . . . . . . . . . . . . . . .
17.4 Custom event objects . . . . . . . . . . . . . . . . . . . .
17.5 Putting it together: Implementing a simple caching system
17.6 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

75
76
76
77
78
79
81

18 Advanced Configuration Tricks
18.1 System configuration . . . . . . .
18.2 Module Configuration . . . . . .
18.3 Configuration mapping table . . .
18.4 Configuration Priority . . . . . .
18.5 Configuration merging workflow

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

83
83
87
88
88
88

19 Using ZendNavigation in your Album Module
19.1 Preparation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
19.2 Setting Up ZendNavigation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

91
91
91

ii

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
19.3 Configuring our Site Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
19.4 Adding the Menu View Helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
19.5 Adding Breadcrumbs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
20 Using ZendPaginator in your Album Module
20.1 Preparation . . . . . . . . . . . . . . . .
20.2 Modifying the AlbumTable . . . . . . .
20.3 Modifying the AlbumController . . . . .
20.4 Updating the View Script . . . . . . . .
20.5 Creating the Pagination Control Partial .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

92
93
93

95
. 95
. 98
. 99
. 99
. 100

21 Using the PaginationControl View Helper

103

22 Setting up a database adapter
105
22.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
22.2 Basic setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
22.3 Setting a static adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
23 Migration from Zend Framework 1

107

24 Namespacing Old Classes
109
24.1 Namespacing a ZF1 Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
24.2 HOWTO Namespace Your Models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
25 Running Zend Framework 2 and Zend Framework 1 in parallel
25.1 Use ZF2 in a ZF1 project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
25.2 Use ZF1 in a ZF2 project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
25.3 Run ZF1 and ZF2 together . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

113
113
114
114

26 Introduction to ZendAuthentication
26.1 Adapters . . . . . . . . . . . .
26.2 Results . . . . . . . . . . . . .
26.3 Identity Persistence . . . . . . .
26.4 Usage . . . . . . . . . . . . . .

117
117
118
119
122

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

27 Database Table Authentication
125
27.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
27.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
27.3 Advanced Usage: Persisting a DbTable Result Object . . . . . . . . . . . . . . . . . . . . . . . . . . 128
28 Digest Authentication
28.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
28.2 Specifics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
28.3 Identity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

131
131
131
131

29 HTTP Authentication Adapter
29.1 Introduction . . . . . . .
29.2 Design Overview . . . . .
29.3 Configuration Options . .
29.4 Resolvers . . . . . . . . .
29.5 Basic Usage . . . . . . .

.
.
.
.
.

133
133
133
134
134
135

30 LDAP Authentication
30.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
30.2 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
30.3 The API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

137
137
137
139

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

iii
30.4 Server Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
30.5 Collecting Debugging Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
30.6 Common Options for Specific Servers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
31 Authentication Validator
145
31.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
31.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
32 Introduction to ZendBarcode

147

33 Barcode creation using ZendBarcodeBarcode class
149
33.1 Using ZendBarcodeBarcode::factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
33.2 Drawing a barcode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
33.3 Rendering a barcode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
34 ZendBarcodeBarcode Objects
153
34.1 Common Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
34.2 Common Additional Getters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
34.3 Description of shipped barcodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
35 ZendBarcode Renderers
163
35.1 Common Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
35.2 ZendBarcodeRendererImage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
35.3 ZendBarcodeRendererPdf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
36 ZendCacheStorageAdapter
36.1 Overview . . . . . . . . . . . . . . .
36.2 Quick Start . . . . . . . . . . . . . .
36.3 Basic Configuration Options . . . . .
36.4 The StorageInterface . . . . . . . . .
36.5 The AvailableSpaceCapableInterface
36.6 The TotalSpaceCapableInterface . . .
36.7 The ClearByNamespaceInterface . .
36.8 The ClearByPrefixInterface . . . . .
36.9 The ClearExpiredInterface . . . . . .
36.10 The FlushableInterface . . . . . . . .
36.11 The IterableInterface . . . . . . . . .
36.12 The OptimizableInterface . . . . . .
36.13 The TaggableInterface . . . . . . . .
36.14 The Apc Adapter . . . . . . . . . . .
36.15 The Dba Adapter . . . . . . . . . . .
36.16 The Filesystem Adapter . . . . . . .
36.17 The Memcached Adapter . . . . . .
36.18 The Memory Adapter . . . . . . . .
36.19 The WinCache Adapter . . . . . . .
36.20 The XCache Adapter . . . . . . . . .
36.21 The ZendServerDisk Adapter . . . .
36.22 The ZendServerShm Adapter . . . .
36.23 Examples . . . . . . . . . . . . . . .

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

165
165
165
166
166
168
168
169
169
169
169
169
170
170
170
171
172
173
174
175
176
177
177
178

37 ZendCacheStorageCapabilities
37.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
37.2 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
37.3 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

181
181
181
183

iv

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
38 ZendCacheStoragePlugin
38.1 Overview . . . . . . . . . . . . .
38.2 Quick Start . . . . . . . . . . . .
38.3 The ClearExpiredByFactor Plugin
38.4 The ExceptionHandler Plugin . .
38.5 The IgnoreUserAbort Plugin . . .
38.6 The OptimizeByFactor Plugin . .
38.7 The Serializer Plugin . . . . . . .
38.8 Available Methods . . . . . . . .
38.9 Examples . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

185
185
185
186
186
186
186
187
187
188

39 ZendCachePattern
189
39.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
39.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
39.3 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
40 ZendCachePatternCallbackCache
40.1 Overview . . . . . . . . . . . .
40.2 Quick Start . . . . . . . . . . .
40.3 Configuration Options . . . . .
40.4 Available Methods . . . . . . .
40.5 Examples . . . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

191
191
191
191
192
192

41 ZendCachePatternClassCache
41.1 Overview . . . . . . . . . .
41.2 Quick Start . . . . . . . . .
41.3 Configuration Options . . .
41.4 Available Methods . . . . .
41.5 Examples . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

193
193
193
193
194
194

42 ZendCachePatternObjectCache
42.1 Overview . . . . . . . . . . .
42.2 Quick Start . . . . . . . . . .
42.3 Configuration Options . . . .
42.4 Available Methods . . . . . .
42.5 Examples . . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

197
197
197
198
198
199

43 ZendCachePatternOutputCache
43.1 Overview . . . . . . . . . . .
43.2 Quick Start . . . . . . . . . .
43.3 Configuration Options . . . .
43.4 Available Methods . . . . . .
43.5 Examples . . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

201
201
201
201
201
202

44 ZendCachePatternCaptureCache
44.1 Overview . . . . . . . . . . . .
44.2 Quick Start . . . . . . . . . . .
44.3 Configuration Options . . . . .
44.4 Available Methods . . . . . . .
44.5 Examples . . . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

203
203
203
204
204
205

45 Introduction to ZendCaptcha

207

46 Captcha Operation

209

v
47 CAPTCHA Adapters
47.1 ZendCaptchaAbstractWord
47.2 ZendCaptchaDumb . . . .
47.3 ZendCaptchaFiglet . . . .
47.4 ZendCaptchaImage . . . .
47.5 ZendCaptchaReCaptcha .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

211
211
212
212
212
213

48 Introduction to ZendConfig
215
48.1 Using ZendConfigConfig with a Reader Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
48.2 Using ZendConfigConfig with a PHP Configuration File . . . . . . . . . . . . . . . . . . . . . . . 216
49 Theory of Operation
50 ZendConfigReader
50.1 ZendConfigReaderIni .
50.2 ZendConfigReaderXml
50.3 ZendConfigReaderJson
50.4 ZendConfigReaderYaml

217

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

219
219
220
222
223

51 ZendConfigWriter
51.1 ZendConfigWriterIni . . . .
51.2 ZendConfigWriterXml . . .
51.3 ZendConfigWriterPhpArray
51.4 ZendConfigWriterJson . . .
51.5 ZendConfigWriterYaml . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

225
225
226
227
228
228

52 ZendConfigProcessor
52.1 ZendConfigProcessorConstant .
52.2 ZendConfigProcessorFilter . .
52.3 ZendConfigProcessorQueue . .
52.4 ZendConfigProcessorToken . .
52.5 ZendConfigProcessorTranslator

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

231
231
232
232
233
233

.
.
.
.

53 The Factory
235
53.1 Loading configuration file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
53.2 Storing configuration file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
54 Introduction to ZendConsole
237
54.1 Writing console routes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
54.2 Handling console requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
54.3 Adding console usage info . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240
55 Console routes and routing
55.1 Router configuration . . .
55.2 Basic route . . . . . . . .
55.3 Catchall route . . . . . . .
55.4 Console routes cheat-sheet

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

243
243
244
248
249

56 Console-aware modules
251
56.1 Application banner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
56.2 Usage information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
56.3 Best practices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
57 Console-aware action controllers
261
57.1 Handling console requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
57.2 Sending output to console . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
vi
57.3 Are we in a console? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
57.4 Reading values from console parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
58 Console adapters
269
58.1 Retrieving console adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
58.2 Using console adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
59 Console prompts
59.1 Confirm . .
59.2 Line . . . .
59.3 Char . . .
59.4 Select . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

273
274
274
275
276

60 Introduction to ZendCrypt

279

61 Encrypt/decrypt using block ciphers

281

62 Key derivation function
283
62.1 Pbkdf2 adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
62.2 SaltedS2k adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
62.3 Scrypt adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
63 Password
287
63.1 Bcrypt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
63.2 Apache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
64 Public key cryptography
291
64.1 Diffie-Hellman . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
64.2 RSA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
65 ZendDbAdapter
65.1 Creating an Adapter - Quickstart . . . . . . . . . . . . . . . . .
65.2 Creating an Adapter Using Dependency Injection . . . . . . . .
65.3 Query Preparation Through ZendDbAdapterAdapter::query()
65.4 Query Execution Through ZendDbAdapterAdapter::query() .
65.5 Creating Statements . . . . . . . . . . . . . . . . . . . . . . .
65.6 Using the Driver Object . . . . . . . . . . . . . . . . . . . . .
65.7 Using The Platform Object . . . . . . . . . . . . . . . . . . . .
65.8 Using The Parameter Container . . . . . . . . . . . . . . . . .
65.9 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.

297
297
298
298
299
299
299
301
302
303

66 ZendDbResultSet
66.1 Quickstart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
66.2 ZendDbResultSetResultSet and ZendDbResultSetAbstractResultSet . . . . . . . . . . . . . . . .
66.3 ZendDbResultSetHydratingResultSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

305
305
306
306

67 ZendDbSql
67.1 ZendDbSqlSql (Quickstart) . . . . . . . . . .
67.2 ZendDbSql’s Select, Insert, Update and Delete
67.3 ZendDbSqlSelect . . . . . . . . . . . . . . . .
67.4 ZendDbSqlInsert . . . . . . . . . . . . . . . .
67.5 ZendDbSqlUpdate . . . . . . . . . . . . . . .
67.6 ZendDbSqlDelete . . . . . . . . . . . . . . .
67.7 ZendDbSqlWhere & ZendDbSqlHaving . .

309
309
310
310
313
314
314
314

68 ZendDbSqlDdl

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

321
vii
69 Creating Tables

323

70 Altering Tables

325

71 Dropping Tables

327

72 Executing DDL Statements

329

73 Currently Supported Data Types

331

74 Currently Supported Constraint Types

333

75 ZendDbTableGateway
335
75.1 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335
75.2 TableGateway Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337
76 ZendDbRowGateway
339
76.1 Quickstart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
76.2 ActiveRecord Style Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
77 ZendDbMetadata
341
77.1 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341
78 Introduction to ZendDi
345
78.1 Dependency Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
78.2 Dependency Injection Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
79 ZendDi Quickstart
80 ZendDi Definition
80.1 DefinitionList . . .
80.2 RuntimeDefinition
80.3 CompilerDefinition
80.4 ClassDefinition . .

347

.
.
.
.

351
351
351
352
353

81 ZendDi InstanceManager
81.1 Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
81.2 Preferences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
81.3 Aliases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

355
355
356
357

82 ZendDi Configuration

359

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

83 ZendDi Debugging & Complex Use Cases
361
83.1 Debugging a DiC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361
83.2 Complex Use Cases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361
84 Introduction to ZendDom

365

85 ZendDomQuery
367
85.1 Theory of Operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367
85.2 Methods Available . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
86 Introduction to ZendEscaper
371
86.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
86.2 What ZendEscaper is not . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372

viii
87 Theory of Operation
373
87.1 The Problem with Inconsistent Functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373
87.2 Why Contextual Escaping? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374
88 Configuring ZendEscaper

377

89 Escaping HTML
379
89.1 Examples of Bad HTML Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
89.2 Examples of Good HTML Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 380
90 Escaping HTML Attributes
381
90.1 Examples of Bad HTML Attribute Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381
90.2 Examples of Good HTML Attribute Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382
91 Escaping Javascript
385
91.1 Examples of Bad Javascript Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
91.2 Examples of Good Javascript Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386
92 Escaping Cascading Style Sheets
387
92.1 Examples of Bad CSS Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
92.2 Examples of Good CSS Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
93 Escaping URLs
389
93.1 Examples of Bad URL Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
93.2 Examples of Good URL Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
94 The EventManager
94.1 Overview . . . . . . .
94.2 Quick Start . . . . . .
94.3 Configuration Options
94.4 Available Methods . .
94.5 Examples . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

391
391
391
394
395
396

95 Introduction to ZendFeed
401
95.1 Reading RSS Feed Data with ZendFeedReader . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401
96 Importing Feeds
403
96.1 Dumping the contents of a feed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
97 Retrieving Feeds from Web Pages
405
97.1 Find Feed Links . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405
98 Consuming an RSS Feed
407
98.1 Reading a feed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407
98.2 Get properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407
99 Consuming an Atom Feed
409
99.1 Basic Use of an Atom Feed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 409
100Consuming a Single Atom Entry
411
100.1 Reading a Single-Entry Atom Feed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411
101ZendFeed and Security
413
101.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
101.2 Filtering data using HTMLPurifier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
101.3 Escaping data using ZendEscaper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 415

ix
102ZendFeedReaderReader
102.1 Introduction . . . . . . . . . . . . . . . . . .
102.2 Importing Feeds . . . . . . . . . . . . . . . .
102.3 Retrieving Underlying Feed and Entry Sources
102.4 Cache Support and Intelligent Requests . . . .
102.5 Locating Feed URIs from Websites . . . . . .
102.6 Attribute Collections . . . . . . . . . . . . . .
102.7 Retrieving Feed Information . . . . . . . . . .
102.8 Retrieving Entry/Item Information . . . . . . .
102.9 Extending Feed and Entry APIs . . . . . . . .

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

417
417
417
418
419
420
421
421
424
426

103ZendFeedWriterWriter
103.1 Introduction . . . . . . .
103.2 Architecture . . . . . . .
103.3 Getting Started . . . . . .
103.4 Setting Feed Data Points .
103.5 Setting Entry Data Points

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

431
431
431
432
434
436

104ZendFeedPubSubHubbub
104.1 What is PubSubHubbub? . . . . . . . .
104.2 Architecture . . . . . . . . . . . . . .
104.3 ZendFeedPubSubHubbubPublisher .
104.4 ZendFeedPubSubHubbubSubscriber

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

439
439
439
440
441

105ZendFileClassFileLocator
105.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
105.2 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
105.3 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

447
447
447
447

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

106Introduction to ZendFilter
449
106.1 What is a filter? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449
106.2 Basic usage of filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449
107Using the StaticFilter
451
107.1 Double filtering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451
108Standard Filter Classes
108.1 Alnum . . . . . . . . . . .
108.2 Alpha . . . . . . . . . . . .
108.3 BaseName . . . . . . . . .
108.4 Boolean . . . . . . . . . . .
108.5 Callback . . . . . . . . . .
108.6 Compress and Decompress .
108.7 Digits . . . . . . . . . . . .
108.8 Dir . . . . . . . . . . . . .
108.9 Encrypt and Decrypt . . . .
108.10HtmlEntities . . . . . . . .
108.11Int . . . . . . . . . . . . . .
108.12Null . . . . . . . . . . . . .
108.13NumberFormat . . . . . . .
108.14PregReplace . . . . . . . .
108.15RealPath . . . . . . . . . .
108.16StringToLower . . . . . . .
108.17StringToUpper . . . . . . .
108.18StringTrim . . . . . . . . .

x

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

453
453
454
454
455
458
459
464
465
465
471
473
473
474
475
476
477
478
478
108.19StripNewLines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479
108.20StripTags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479
108.21UriNormalize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 481
109Word Filters
109.1 CamelCaseToDash . . . .
109.2 CamelCaseToSeparator .
109.3 CamelCaseToUnderscore
109.4 DashToCamelCase . . . .
109.5 DashToSeparator . . . . .
109.6 DashToUnderscore . . . .
109.7 SeparatorToCamelCase .
109.8 SeparatorToDash . . . . .
109.9 SeparatorToSeparator . .
109.10UnderscoreToCamelCase
109.11UnderscoreToSeparator .
109.12UnderscoreToDash . . . .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

483
483
483
484
484
485
485
486
486
487
488
488
489

110File Filter Classes
110.1 Decrypt . . . .
110.2 Encrypt . . . .
110.3 Lowercase . .
110.4 Rename . . . .
110.5 RenameUpload
110.6 Uppercase . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

491
491
491
491
491
493
494

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

111Filter Chains
495
111.1 Setting Filter Chain Order . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495
111.2 Using the Plugin Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495
112ZendFilterInflector
112.1 Operation . . . . . . . . . . . . . . . . . . . . . . . . . .
112.2 Using Custom Filters . . . . . . . . . . . . . . . . . . . .
112.3 Setting the Inflector Target . . . . . . . . . . . . . . . . .
112.4 Inflection Rules . . . . . . . . . . . . . . . . . . . . . . .
112.5 Utility Methods . . . . . . . . . . . . . . . . . . . . . . .
112.6 Using a Traversable or an array with ZendFilterInflector

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

497
497
498
498
499
501
501

113Writing Filters

503

114Introduction to ZendForm

505

115Form Quick Start
115.1 Programmatic Form Creation .
115.2 Creation via Factory . . . . . .
115.3 Factory-backed Form Extension
115.4 Validating Forms . . . . . . . .
115.5 Hinting to the Input Filter . . .
115.6 Binding an object . . . . . . . .
115.7 Rendering . . . . . . . . . . .
115.8 Validation Groups . . . . . . .
115.9 Using Annotations . . . . . . .

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

507
507
508
512
513
514
516
517
520
521

116Form Collections
525
116.1 Creating Fieldsets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 528

xi
116.2
116.3
116.4
116.5
116.6

The Form Element . . . . . . . . . . . . . .
The Controller . . . . . . . . . . . . . . . .
The View . . . . . . . . . . . . . . . . . . .
Adding New Elements Dynamically . . . . .
Validation groups for fieldsets and collection

117File Uploading
117.1 Standard Example . . . . . .
117.2 File Post-Redirect-Get Plugin
117.3 HTML5 Multi-File Uploads .
117.4 Upload Progress . . . . . . .
117.5 Additional Info . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

532
533
534
535
537

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

541
541
544
546
547
551

118Advanced use of forms
118.1 Short names . . . . . . . . . .
118.2 Creating custom elements . . .
118.3 Handling dependencies . . . . .
118.4 The specific case of initializers .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

553
553
553
557
559

119Form Elements
119.1 Introduction . . . .
119.2 Element Base Class
119.3 Standard Elements .
119.4 HTML5 Elements .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

561
561
561
563
578

120Form View Helpers
120.1 Introduction . . . . . . . . .
120.2 Standard Helpers . . . . . . .
120.3 HTML5 Helpers . . . . . . .
120.4 File Upload Progress Helpers

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

593
593
593
605
609

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

121Overview of ZendHttp
611
121.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 611
121.2 ZendHttp Request, Response and Headers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 611
122The Request Class
122.1 Overview . . . . . . .
122.2 Quick Start . . . . . .
122.3 Configuration Options
122.4 Available Methods . .
122.5 Examples . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

613
613
613
614
614
617

123The Response Class
123.1 Overview . . . . . . .
123.2 Quick Start . . . . . .
123.3 Configuration Options
123.4 Available Methods . .
123.5 Examples . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

619
619
619
620
620
623

124The Headers Class
124.1 Overview . . . . . . . . . . . . . .
124.2 Quick Start . . . . . . . . . . . . .
124.3 Configuration Options . . . . . . .
124.4 Available Methods . . . . . . . . .
124.5 ZendHttpHeader* Base Methods

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

625
625
625
626
626
628

xii
124.6 List of HTTP Header Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 628
124.7 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 629
125HTTP Client - Overview
125.1 Overview . . . . . . .
125.2 Quick Start . . . . . .
125.3 Configuration Options
125.4 Available Methods . .
125.5 Examples . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

633
633
633
634
634
638

126HTTP Client - Connection Adapters
126.1 Overview . . . . . . . . . . . . . . . .
126.2 The Socket Adapter . . . . . . . . . .
126.3 The Proxy Adapter . . . . . . . . . . .
126.4 The cURL Adapter . . . . . . . . . . .
126.5 The Test Adapter . . . . . . . . . . . .
126.6 Creating your own connection adapters

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

641
641
641
644
645
646
648

127HTTP Client - Advanced Usage
127.1 HTTP Redirections . . . . . . . . . . . . . . . .
127.2 Adding Cookies and Using Cookie Persistence .
127.3 Setting Custom Request Headers . . . . . . . .
127.4 File Uploads . . . . . . . . . . . . . . . . . . .
127.5 Sending Raw POST Data . . . . . . . . . . . .
127.6 HTTP Authentication . . . . . . . . . . . . . .
127.7 Sending Multiple Requests With the Same Client
127.8 Data Streaming . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

651
651
651
653
654
654
655
655
656

128HTTP Client - Static Usage
128.1 Overview . . . . . . .
128.2 Quick Start . . . . . .
128.3 Configuration Options
128.4 Available Methods . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

659
659
659
659
660

129Translating
129.1 Adding translations .
129.2 Supported formats .
129.3 Setting a locale . . .
129.4 Translating messages
129.5 Caching . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

661
661
662
662
662
662

130I18n View Helpers
130.1 Introduction . . . . . . .
130.2 CurrencyFormat Helper .
130.3 DateFormat Helper . . . .
130.4 NumberFormat Helper . .
130.5 Plural Helper . . . . . . .
130.6 Translate Helper . . . . .
130.7 TranslatePlural Helper . .
130.8 Abstract Translator Helper

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

663
663
663
665
666
667
668
669
670

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

131I18n Filters
671
131.1 Alnum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 671
131.2 Alpha . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672
131.3 NumberFormat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672

xiii
131.4 NumberParse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 673
132I18n Validators

675

133Float
677
133.1 Supported options for ZendI18nValidatorFloat . . . . . . . . . . . . . . . . . . . . . . . . . . . . 677
133.2 Simple float validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 677
133.3 Localized float validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 677
134Int
679
134.1 Supported options for ZendI18nValidatorInt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 679
134.2 Simple integer validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 679
134.3 Localized integer validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 679
135Introduction to ZendInputFilter

681

136File Upload Input

685

137Introduction to ZendJson

687

138Basic Usage
689
138.1 Pretty-printing JSON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 689
139Advanced Usage of ZendJson
139.1 JSON Objects . . . . . .
139.2 Encoding PHP objects . .
139.3 Internal Encoder/Decoder
139.4 JSON Expressions . . . .

.
.
.
.

140XML to JSON conversion

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

691
691
691
692
692
693

141ZendJsonServer - JSON-RPC server
695
141.1 Advanced Details . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 697
142Introduction to ZendLdap
703
142.1 Theory of operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 703
143API overview
707
143.1 Configuration / options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 707
143.2 API Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 708
144ZendLdapLdap
709
144.1 ZendLdapCollection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 710
145ZendLdapAttribute

711

146ZendLdapConverterConverter

713

147ZendLdapDn

715

148ZendLdapFilter

717

149ZendLdapNode

719

150ZendLdapNodeRootDse
721
150.1 OpenLDAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 723
150.2 ActiveDirectory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 723

xiv
150.3 eDirectory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 724
151ZendLdapNodeSchema
727
151.1 OpenLDAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 729
151.2 ActiveDirectory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 730
152ZendLdapLdifEncoder

731

153Usage Scenarios
733
153.1 Authentication scenarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 733
153.2 Basic CRUD operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 733
153.3 Extended operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 735
154Tools
154.1 Creation and modification of DN strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
154.2 Using the filter API to create search filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
154.3 Modify LDAP entries using the Attribute API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

737
737
737
737

155Object-oriented access to the LDAP tree using ZendLdapNode
155.1 Basic CRUD operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
155.2 Extended operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
155.3 Tree traversal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

739
739
739
739

156Getting information from the LDAP server
741
156.1 RootDSE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 741
156.2 Schema Browsing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 741
157Serializing LDAP data to and from LDIF
743
157.1 Serialize a LDAP entry to LDIF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 743
157.2 Deserialize a LDIF string into a LDAP entry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 744
158The AutoloaderFactory
158.1 Overview . . . . . . .
158.2 Quick Start . . . . . .
158.3 Configuration Options
158.4 Available Methods . .
158.5 Examples . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

747
747
747
748
748
748

159The StandardAutoloader
159.1 Overview . . . . . . .
159.2 Quick Start . . . . . .
159.3 Configuration Options
159.4 Available Methods . .
159.5 Examples . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

749
749
750
751
751
752

160The ClassMapAutoloader
160.1 Overview . . . . . . .
160.2 Quick Start . . . . . .
160.3 Configuration Options
160.4 Available Methods . .
160.5 Examples . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

753
753
753
754
754
755

161The ModuleAutoloader
161.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
161.2 Quickstart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
161.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

757
757
757
757
xv
161.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 758
161.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 758
162The SplAutoloader Interface
162.1 Overview . . . . . . . .
162.2 Quick Start . . . . . . .
162.3 Configuration Options .
162.4 Available Methods . . .
162.5 Examples . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

759
759
759
760
760
761

163The PluginClassLoader
163.1 Overview . . . . . . .
163.2 Quick Start . . . . . .
163.3 Configuration Options
163.4 Available Methods . .
163.5 Examples . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

763
763
763
764
764
765

164The ShortNameLocator Interface
164.1 Overview . . . . . . . . . . .
164.2 Quick Start . . . . . . . . . .
164.3 Configuration Options . . . .
164.4 Available Methods . . . . . .
164.5 Examples . . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

769
769
769
770
770
770

165The PluginClassLocator interface
165.1 Overview . . . . . . . . . . .
165.2 Quick Start . . . . . . . . . .
165.3 Configuration Options . . . .
165.4 Available Methods . . . . . .
165.5 Examples . . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

771
771
771
771
771
772

.
.
.
.
.

166The Class Map Generator utility: bin/classmap_generator.php
773
166.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 773
166.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 773
166.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 773
167Overview of ZendLog
167.1 Creating a Log . . . . . .
167.2 Logging Messages . . . .
167.3 Destroying a Log . . . . .
167.4 Using Built-in Priorities .
167.5 Understanding Log Events
167.6 Log PHP Errors . . . . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

775
775
776
776
776
777
777

168Writers
168.1 Writing to Streams . . .
168.2 Writing to Databases . .
168.3 Writing to FirePHP . . .
168.4 Stubbing Out the Writer
168.5 Testing with the Mock .
168.6 Compositing Writers . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

779
779
780
781
781
781
782

.
.
.
.
.
.

169Filters
783
169.1 Available filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 783

xvi
170Formatters
170.1 Simple Formatting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
170.2 Formatting to XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
170.3 Formatting to FirePhp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

785
785
786
786

171Introduction to ZendMail
787
171.1 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 787
171.2 Configuring the default sendmail transport . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 788
172ZendMailMessage
172.1 Overview . . . . . . .
172.2 Quick Start . . . . . .
172.3 Configuration Options
172.4 Available Methods . .
172.5 Examples . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

789
789
789
791
791
794

173ZendMailTransport
173.1 Overview . . . . . . .
173.2 Quick Start . . . . . .
173.3 Configuration Options
173.4 Available Methods . .
173.5 Examples . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

795
795
795
796
797
797

174ZendMailTransportSmtpOptions
174.1 Overview . . . . . . . . . . . .
174.2 Quick Start . . . . . . . . . . .
174.3 Configuration Options . . . . .
174.4 Available Methods . . . . . . .
174.5 Examples . . . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

799
799
799
801
801
802

175ZendMailTransportFileOptions
175.1 Overview . . . . . . . . . . .
175.2 Quick Start . . . . . . . . . .
175.3 Configuration Options . . . .
175.4 Available Methods . . . . . .
175.5 Examples . . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

803
803
803
803
804
804

.
.
.
.
.

176Introduction to ZendMath
805
176.1 Random number generator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 805
176.2 Big integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 806
177ZendMime
809
177.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 809
177.2 Static Methods and Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 809
177.3 Instantiating ZendMime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 810
178ZendMimeMessage
178.1 Introduction . . . . . . . . . . . . . . . . . . . . . .
178.2 Instantiation . . . . . . . . . . . . . . . . . . . . . .
178.3 Adding MIME Parts . . . . . . . . . . . . . . . . . .
178.4 Boundary handling . . . . . . . . . . . . . . . . . . .
178.5 Parsing a string to create a ZendMimeMessage object
178.6 Available methods . . . . . . . . . . . . . . . . . . .
179ZendMimePart

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

811
811
811
811
811
812
812
813

xvii
179.1
179.2
179.3
179.4

Introduction . . . . . . . . . . . . . . . . . . . .
Instantiation . . . . . . . . . . . . . . . . . . . .
Methods for rendering the message part to a string
Available methods . . . . . . . . . . . . . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

813
813
813
814

180Introduction to the Module System
815
180.1 The autoload_*.php Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 816
181The Module Manager
817
181.1 Module Manager Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 817
181.2 Module Manager Listeners . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 817
182The Module Class
182.1 A Minimal Module . . . . . . .
182.2 A Typical Module Class . . . .
182.3 The “loadModules.post” Event
182.4 The MVC “bootstrap” Event . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

821
821
821
822
823

183The Module Autoloader
825
183.1 Module Autoloader Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 825
183.2 Non-Standard / Explicit Module Paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 826
183.3 Packaging Modules with Phar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 827
184Best Practices when Creating Modules

829

185Introduction to the MVC Layer
185.1 Basic Application Structure . . . . .
185.2 Basic Module Structure . . . . . . .
185.3 Bootstrapping an Application . . . .
185.4 Bootstrapping a Modular Application
185.5 Conclusion . . . . . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

831
832
832
834
836
837

186Quick Start
186.1 Install the Zend Skeleton Application .
186.2 Create a New Module . . . . . . . . .
186.3 Update the Module Class . . . . . . . .
186.4 Create a Controller . . . . . . . . . . .
186.5 Create a View Script . . . . . . . . . .
186.6 Create a Route . . . . . . . . . . . . .
186.7 Tell the Application About our Module
186.8 Test it Out! . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

839
839
840
840
841
842
842
844
845

187Default Services
187.1 Theory of Operation . . . . . . . .
187.2 ServiceManager . . . . . . . . . .
187.3 Abstract Factories . . . . . . . . .
187.4 Plugin Managers . . . . . . . . . .
187.5 ViewManager . . . . . . . . . . . .
187.6 Application Configuration Options
187.7 Default Configuration Options . . .

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

847
847
847
852
854
855
856
857

.
.
.
.
.
.
.

.
.
.
.
.
.
.

188Routing
861
188.1 Router Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 863
188.2 HTTP Route Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 863
188.3 HTTP Routing Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 869

xviii
188.4 Console Route Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 871
189The MvcEvent
189.1 Order of events . . . . . . . . . . . . . . .
189.2 MvcEvent::EVENT_BOOTSTRAP . . . .
189.3 MvcEvent::EVENT_ROUTE . . . . . . .
189.4 MvcEvent::EVENT_DISPATCH . . . . .
189.5 MvcEvent::EVENT_DISPATCH_ERROR
189.6 MvcEvent::EVENT_RENDER . . . . . .
189.7 MvcEvent::EVENT_RENDER_ERROR .
189.8 MvcEvent::EVENT_FINISH . . . . . . .

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

873
874
874
875
876
877
879
879
880

190The SendResponseEvent
883
190.1 Listeners . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 883
190.2 Triggerers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 884
191Available Controllers
885
191.1 Common Interfaces Used With Controllers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 885
191.2 The AbstractActionController . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 887
191.3 The AbstractRestfulController . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 888
192Controller Plugins
192.1 AcceptableViewModelSelector Plugin
192.2 FlashMessenger Plugin . . . . . . . .
192.3 Forward Plugin . . . . . . . . . . . .
192.4 Identity Plugin . . . . . . . . . . . .
192.5 Layout Plugin . . . . . . . . . . . .
192.6 Params Plugin . . . . . . . . . . . .
192.7 Post/Redirect/Get Plugin . . . . . . .
192.8 File Post/Redirect/Get Plugin . . . .
192.9 Redirect Plugin . . . . . . . . . . . .
192.10Url Plugin . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

891
891
892
894
894
895
895
896
896
898
898

193Examples
899
193.1 Controllers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 899
193.2 Bootstrapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 900
194Introduction to ZendNavigation
903
194.1 Pages and Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 903
194.2 View Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 904
195Quick Start

905

196Pages

907

197Common page features

909

198ZendNavigationPageMvc

913

199ZendNavigationPageUri

917

200Creating custom page types

919

201Creating pages using the page factory

921

xix
202Containers
202.1 Creating containers
202.2 Adding pages . . .
202.3 Removing pages .
202.4 Finding pages . . .
202.5 Iterating containers
202.6 Other operations .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

923
923
929
930
931
933
934

203View Helpers
203.1 Introduction . . . . . . . . . . . .
203.2 Translation of labels and titles . . .
203.3 Integration with ACL . . . . . . . .
203.4 Navigation setup used in examples

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

937
937
938
939
939

204View Helper - Breadcrumbs
204.1 Introduction . . . . . . . . . . . . .
204.2 Basic usage . . . . . . . . . . . . . .
204.3 Specifying indentation . . . . . . . .
204.4 Customize output . . . . . . . . . . .
204.5 Rendering using a partial view script

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

945
945
945
946
946
947

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

205View Helper - Links
949
205.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 949
205.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 951
206View Helper - Menu
206.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
206.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
206.3 Calling renderMenu() directly . . . . . . . . . . . . . . . . . . . . . .
206.4 Rendering the deepest active menu . . . . . . . . . . . . . . . . . . .
206.5 Rendering with maximum depth . . . . . . . . . . . . . . . . . . . . .
206.6 Rendering with minimum depth . . . . . . . . . . . . . . . . . . . . .
206.7 Rendering only the active branch . . . . . . . . . . . . . . . . . . . .
206.8 Rendering only the active branch with minimum depth . . . . . . . . .
206.9 Rendering only the active branch with maximum depth . . . . . . . . .
206.10Rendering only the active branch with maximum depth and no parents .
206.11Rendering a custom menu using a partial view script . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

955
955
956
957
958
958
959
960
961
961
962
962

207View Helper - Sitemap
207.1 Introduction . . . . . . . . . . . .
207.2 Basic usage . . . . . . . . . . . . .
207.3 Rendering using no ACL role . . .
207.4 Rendering using a maximum depth

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

965
965
966
967
968

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

208View Helper - Navigation Proxy
971
208.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 971
208.2 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 971
209Introduction to ZendPaginator

973

210Usage
210.1 Paginating data collections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
210.2 The DbSelect adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
210.3 Rendering pages with view scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

975
975
976
977

xx
211Configuration

983

212Advanced usage
212.1 Custom data source adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
212.2 Custom scrolling styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
212.3 Caching features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

985
985
985
986

213Introduction to ZendPermissionsAcl
213.1 Resources . . . . . . . . . . . . .
213.2 Roles . . . . . . . . . . . . . . .
213.3 Creating the Access Control List .
213.4 Registering Roles . . . . . . . . .
213.5 Defining Access Controls . . . .
213.6 Querying an ACL . . . . . . . .

989
989
990
991
991
992
993

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

214Refining Access Controls
995
214.1 Precise Access Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 995
214.2 Removing Access Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 997
215Advanced Usage
999
215.1 Storing ACL Data for Persistence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 999
215.2 Writing Conditional ACL Rules with Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 999
216Introduction to ZendPermissionsRbac
1001
216.1 Roles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1001
216.2 Permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1001
216.3 Dynamic Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1001
217Methods

1003

218Examples
218.1 Roles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
218.2 Permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
218.3 Dynamic Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1005
. 1005
. 1006
. 1006

219Progress Bars
219.1 Introduction . . .
219.2 Basic Usage . . .
219.3 Persistent Progress
219.4 Standard Adapters

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

220File Upload Handlers
220.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
220.2 Methods of Reporting Progress . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
220.3 Standard Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1009
1009
1009
1009
1010

1013
. 1013
. 1013
. 1014

221Introduction to ZendSerializer
1017
221.1 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1017
221.2 Basic configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1018
221.3 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1018
222ZendSerializerAdapter
222.1 The PhpSerialize Adapter
222.2 The IgBinary Adapter . .
222.3 The Wddx Adapter . . . .
222.4 The Json Adapter . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

1021
1021
1021
1021
1022
xxi
222.5 The PythonPickle Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1022
222.6 The PhpCode Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1023
223Introduction to ZendServer

1025

224ZendServerReflection
1027
224.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1027
224.2 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1027
225ZendServiceManager

1029

226ZendServiceManager Quick Start
1033
226.1 Using Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1033
226.2 Modules as Service Providers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1034
226.3 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1034
227Delegator service factories
1039
227.1 Delegator factory signature . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1039
227.2 A Delegator factory use case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1039
228Lazy Services
228.1 Use cases . . . . .
228.2 Setup . . . . . . .
228.3 Practical example .
228.4 Configuration . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

1043
1043
1043
1043
1045

229Session Config
1047
229.1 Standard Config . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1047
229.2 Session Config . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1048
229.3 Custom Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1049
230Session Container
1051
230.1 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1051
230.2 Setting the Default Session Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1051
231Session Manager
1053
231.1 Initializing the Session Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1053
231.2 Session Compatibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1055
232Session Save Handlers
232.1 Cache . . . . . . . . .
232.2 DbTableGateway . . .
232.3 MongoDB . . . . . .
232.4 Custom Save Handlers

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

1057
1057
1057
1058
1059

233Session Storage
233.1 Array Storage . . . . .
233.2 Session Storage . . . .
233.3 Session Array Storage
233.4 Custom Storage . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

1061
1061
1061
1062
1062

234Session Validators
1063
234.1 Http User Agent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1063
234.2 Remote Addr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1063
234.3 Custom Validators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1064

xxii
235ZendSoapServer
235.1 ZendSoapServer constructor . . . . .
235.2 Methods to define Web Service API . .
235.3 Request and response objects handling
235.4 Document/Literal WSDL Handling . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

1065
1065
1066
1067
1069

236ZendSoapClient
1071
236.1 ZendSoapClient Constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1071
236.2 Performing SOAP Requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1072
237WSDL Accessor
237.1 ZendSoapWsdl constructor . .
237.2 addMessage() method . . . . .
237.3 addPortType() method . . . . .
237.4 addPortOperation() method . .
237.5 addBinding() method . . . . . .
237.6 addBindingOperation() method
237.7 addSoapBinding() method . . .
237.8 addSoapOperation() method . .
237.9 addService() method . . . . . .
237.10Type mapping . . . . . . . . .
237.11addDocumentation() method . .
237.12Get finalized WSDL document

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1075
1075
1075
1076
1076
1076
1077
1077
1077
1077
1078
1079
1080

238AutoDiscovery
238.1 AutoDiscovery Introduction
238.2 Class autodiscovering . . .
238.3 Functions autodiscovering .
238.4 Autodiscovering Datatypes .
238.5 WSDL Binding Styles . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

1081
1081
1082
1083
1083
1083

.
.
.
.
.

.
.
.
.
.

239ZendStdlibHydrator
239.1 HydratorInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
239.2 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
239.3 Available Implementations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1085
. 1085
. 1085
. 1086

240ZendStdlibHydratorFilter
240.1 Filter implementations . . . . . . . .
240.2 Remove filters . . . . . . . . . . . .
240.3 Add filters . . . . . . . . . . . . . .
240.4 Use the composite for complex filters
240.5 Using the provider interface . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

1087
1087
1088
1088
1089
1090

241ZendStdlibHydratorStrategy
1093
241.1 Adding strategies to the hydrators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1093
241.2 Available implementations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1094
241.3 Writing custom strategies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1095
242ZendStdlibHydratorAggregateAggregateHydrator
1097
242.1 Installation requirements for the AggregateHydrator . . . . . . . . . . . . . . . . . . . . . . . . . . 1097
242.2 Example of AggregateHydrator usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1097
242.3 Advanced use cases of the AggregateHydrator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1098
243Introduction to ZendTag

1101

xxiii
244Creating tag clouds with ZendTagCloud
1103
244.1 Decorators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1105
245Introduction to ZendTest

1109

246Unit testing with PHPUnit

1111

247Setup your TestCase

1113

248Testing your Controllers and MVC Applications

1115

249Assertions

1117

250Request Assertions

1119

251CSS Selector Assertions

1121

252XPath Assertions

1123

253Redirect Assertions

1125

254Response Header Assertions

1127

255ZendTextFiglet
1129
255.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1129
255.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1130
256ZendTextTable
1131
256.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1131
256.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1132
257ZendUri
257.1 Overview . . . . . . . . . . .
257.2 Creating a New URI . . . . .
257.3 Manipulating an Existing URI
257.4 Common Instance Methods .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

1133
1133
1133
1134
1134

258Introduction to ZendValidator
258.1 What is a validator? . . .
258.2 Basic usage of validators .
258.3 Customizing messages . .
258.4 Translating messages . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

1139
1139
1139
1140
1141

.
.
.
.

.
.
.
.

259Standard Validation Classes

1143

260Alnum
260.1 Supported options for ZendI18nValidatorAlnum
260.2 Basic usage . . . . . . . . . . . . . . . . . . . .
260.3 Using whitespaces . . . . . . . . . . . . . . . .
260.4 Using different languages . . . . . . . . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

1145
1145
1145
1145
1146

261Alpha
261.1 Supported options for ZendValidatorAlpha
261.2 Basic usage . . . . . . . . . . . . . . . . . .
261.3 Using whitespaces . . . . . . . . . . . . . .
261.4 Using different languages . . . . . . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

1147
1147
1147
1147
1148

xxiv

.
.
.
.

.
.
.
.
262Barcode
262.1 Supported options for ZendValidatorBarcode
262.2 Basic usage . . . . . . . . . . . . . . . . . . .
262.3 Optional checksum . . . . . . . . . . . . . . .
262.4 Writing custom adapters . . . . . . . . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

1149
1151
1152
1152
1152

263Between
1155
263.1 Supported options for ZendValidatorBetween . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1155
263.2 Default behaviour for ZendValidatorBetween . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1155
263.3 Validation exclusive the border values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1155
264Callback
264.1 Supported options for ZendValidatorCallback
264.2 Basic usage . . . . . . . . . . . . . . . . . . .
264.3 Usage with closures . . . . . . . . . . . . . .
264.4 Usage with class-based callbacks . . . . . . .
264.5 Adding options . . . . . . . . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

1157
1157
1157
1157
1158
1159

265CreditCard
265.1 Supported options for ZendValidatorCreditCard
265.2 Basic usage . . . . . . . . . . . . . . . . . . . .
265.3 Accepting defined credit cards . . . . . . . . . .
265.4 Validation by using foreign APIs . . . . . . . .
265.5 Ccnum . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

1161
1162
1162
1162
1163
1164

266Date
266.1 Supported options for ZendValidatorDate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
266.2 Default date validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
266.3 Self defined date validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1165
. 1165
. 1165
. 1165

267DbRecordExists and DbNoRecordExists
267.1 Supported options for ZendValidatorDb*
267.2 Basic usage . . . . . . . . . . . . . . . . .
267.3 Excluding records . . . . . . . . . . . . .
267.4 Database Schemas . . . . . . . . . . . . .
267.5 Using a Select object . . . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

1167
1167
1167
1168
1169
1170

268Digits
1171
268.1 Supported options for ZendValidatorDigits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1171
268.2 Validating digits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1171
269EmailAddress
269.1 Basic usage . . . . . . . . . . . . . . . . . . . .
269.2 Options for validating Email Addresses . . . . .
269.3 Complex local parts . . . . . . . . . . . . . . .
269.4 Validating only the local part . . . . . . . . . . .
269.5 Validating different types of hostnames . . . . .
269.6 Checking if the hostname actually accepts email
269.7 Validating International Domains Names . . . .
269.8 Validating Top Level Domains . . . . . . . . . .
269.9 Setting messages . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

1173
1173
1173
1174
1174
1174
1174
1175
1176
1176

270File Validation Classes
1177
270.1 Crc32 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1177
270.2 ExcludeExtension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1178

xxv
270.3 ExcludeMimeType
270.4 Exists . . . . . . .
270.5 Extension . . . . .
270.6 Hash . . . . . . .
270.7 ImageSize . . . .
270.8 IsCompressed . .
270.9 IsImage . . . . . .
270.10Md5 . . . . . . . .
270.11MimeType . . . .
270.12NotExists . . . . .
270.13Sha1 . . . . . . .
270.14Size . . . . . . . .
270.15UploadFile . . . .
270.16WordCount . . . .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

271Float
271.1 Supported options for ZendI18nValidatorFloat . . . . . . . . . . . . . . . . . . . . . . . . . . .
271.2 Simple float validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
271.3 Localized float validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

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

1178
1178
1179
1180
1180
1181
1182
1182
1183
1184
1185
1186
1186
1187

1189
. 1189
. 1189
. 1189

272GreaterThan
1191
272.1 Supported options for ZendValidatorGreaterThan . . . . . . . . . . . . . . . . . . . . . . . . . . . 1191
272.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1191
272.3 Validation inclusive the border value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1191
273Hex
273.1 Supported options for ZendValidatorHex

1193
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1193

274Hostname
274.1 Supported options for ZendValidatorHostname
274.2 Basic usage . . . . . . . . . . . . . . . . . . . .
274.3 Validating different types of hostnames . . . . .
274.4 Validating International Domains Names . . . .
274.5 Validating Top Level Domains . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

1195
1195
1195
1196
1196
1197

275Iban
1199
275.1 Supported options for ZendValidatorIban . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1199
275.2 IBAN validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1199
276Identical
276.1 Supported options for ZendValidatorIdentical
276.2 Basic usage . . . . . . . . . . . . . . . . . . .
276.3 Identical objects . . . . . . . . . . . . . . . .
276.4 Form elements . . . . . . . . . . . . . . . . .
276.5 Strict validation . . . . . . . . . . . . . . . . .
276.6 Configuration . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

1201
1201
1201
1201
1202
1204
1204

277InArray
277.1 Supported options for ZendValidatorInArray
277.2 Simple array validation . . . . . . . . . . . . .
277.3 Array validation modes . . . . . . . . . . . .
277.4 Recursive array validation . . . . . . . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

1207
1207
1208
1208
1209

278Ip
1211
278.1 Supported options for ZendValidatorIp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1211

xxvi
278.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1211
278.3 Validate IPv4 or IPV6 alone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1212
279Isbn
279.1
279.2
279.3
279.4

Supported options for ZendValidatorIsbn
Basic usage . . . . . . . . . . . . . . . . .
Setting an explicit ISBN validation type . .
Specifying a separator restriction . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

280LessThan
280.1 Supported options for ZendValidatorLessThan . . . . . . . . . . . . . . . . . . . . . . . . . . . .
280.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
280.3 Validation inclusive the border value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.

1213
1213
1213
1213
1214

1215
. 1215
. 1215
. 1215

281NotEmpty
1217
281.1 Supported options for ZendValidatorNotEmpty . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1217
281.2 Default behaviour for ZendValidatorNotEmpty . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1217
281.3 Changing behaviour for ZendValidatorNotEmpty . . . . . . . . . . . . . . . . . . . . . . . . . . . 1217
282PostCode
1219
282.1 Constructor options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1220
282.2 Supported options for ZendValidatorPostCode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1220
283Regex
1221
283.1 Supported options for ZendValidatorRegex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1221
283.2 Validation with ZendValidatorRegex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1221
283.3 Pattern handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1221
284Sitemap Validators
284.1 SitemapChangefreq . . . . . . . . . . . . . . .
284.2 SitemapLastmod . . . . . . . . . . . . . . . . .
284.3 SitemapLoc . . . . . . . . . . . . . . . . . . .
284.4 SitemapPriority . . . . . . . . . . . . . . . . .
284.5 Supported options for ZendValidatorSitemap_*

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

1223
1223
1223
1224
1224
1224

285Step
285.1 Supported options for ZendValidatorStep . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
285.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
285.3 Using floating-point values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1225
. 1225
. 1225
. 1225

286StringLength
286.1 Supported options for ZendValidatorStringLength
286.2 Default behaviour for ZendValidatorStringLength
286.3 Limiting the maximum allowed length of a string .
286.4 Limiting the minimal required length of a string .
286.5 Limiting a string on both sides . . . . . . . . . . .
286.6 Encoding of values . . . . . . . . . . . . . . . . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

1227
1227
1227
1227
1228
1228
1228

287File Validation Classes
287.1 Crc32 . . . . . . .
287.2 ExcludeExtension
287.3 ExcludeMimeType
287.4 Exists . . . . . . .
287.5 Extension . . . . .
287.6 Hash . . . . . . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

1231
1231
1232
1232
1232
1233
1234

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

xxvii
287.7 ImageSize . .
287.8 IsCompressed
287.9 IsImage . . . .
287.10Md5 . . . . . .
287.11MimeType . .
287.12NotExists . . .
287.13Sha1 . . . . .
287.14Size . . . . . .
287.15UploadFile . .
287.16WordCount . .

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

1234
1235
1236
1236
1237
1238
1239
1240
1240
1241

288Validator Chains

1243

289Writing Validators

1245

290Validation Messages
1249
290.1 Using pre-translated validation messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1249
290.2 Limit the size of a validation message . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1250
291Getting the Zend Framework Version

1251

292ZendView Quick Start
1253
292.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1253
292.2 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1253
293The PhpRenderer
293.1 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
293.2 Options and Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
293.3 Additional Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1267
. 1267
. 1271
. 1271

294PhpRenderer View Scripts
1273
294.1 Escaping Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1274
295The ViewEvent
295.1 Order of events . . . . . . . . . . . . . . .
295.2 ViewEvent::EVENT_RENDERER . . . .
295.3 ViewEvent::EVENT_RENDERER_POST
295.4 ViewEvent::EVENT_RESPONSE . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

1275
1275
1276
1277
1277

296View Helpers
1279
296.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1279
296.2 Included Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1280
297View Helper - BasePath
1281
297.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1281
297.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1281
298View Helper - Cycle
1283
298.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1283
298.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1283
298.3 Working with two or more cycles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1284
299View Helper - Doctype
1285
299.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1285
299.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1286
299.3 Retrieving the Doctype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1286

xxviii
299.4 Choosing a Doctype to Use with the Open Graph Protocol . . . . . . . . . . . . . . . . . . . . . . . 1286
299.5 Zend MVC View Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1287
300FlashMessenger Helper
300.1 Introduction . . . . . . . . . . . . . . . . .
300.2 Basic Usage . . . . . . . . . . . . . . . . .
300.3 CSS Layout . . . . . . . . . . . . . . . . . .
300.4 HTML Layout . . . . . . . . . . . . . . . .
300.5 Sample Modification for Twitter Bootstrap 3

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

1289
1289
1289
1289
1290
1290

301View Helper - HeadLink
1293
301.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1293
301.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1294
302View Helper - HeadMeta
302.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
302.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
302.3 Usage with XHTML1_RDFA doctype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1295
. 1295
. 1296
. 1296

303View Helper - HeadScript
303.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
303.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
303.3 Capturing Scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1299
. 1299
. 1301
. 1301

304View Helper - HeadStyle
1303
304.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1303
304.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1306
304.3 Capturing Style Declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1306
305View Helper - HeadTitle
1309
305.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1309
305.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1309
306View Helper - HtmlList
1311
306.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1311
306.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1311
307View Helper - HTML Object
1315
307.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1315
307.2 Flash helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1315
307.3 Customizing the object by passing additional arguments . . . . . . . . . . . . . . . . . . . . . . . . 1316
308View Helper - Identity
1317
308.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1317
308.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1317
308.3 Using with ServiceManager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1317
309View Helper - InlineScript
1319
309.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1319
309.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1319
309.3 Capturing Scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1320
310View Helper - JSON
1321
310.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1321
310.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1321

xxix
311View Helper - Partial
1323
311.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1323
311.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1323
311.3 Using PartialLoop to Render Iterable Models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1324
312View Helper - Placeholder
312.1 Introduction . . . . . . .
312.2 Basic Usage . . . . . . .
312.3 Aggregate Content . . . .
312.4 Capture Content . . . . .
312.5 Concrete Implementations

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

1327
1327
1327
1327
1328
1329

313View Helper - URL
1331
313.1 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1331
314Advanced usage of helpers
1333
314.1 Registering Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1333
314.2 Writing Custom Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1334
314.3 Registering Concrete Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1336
315Introduction to ZendXmlRpc
1337
315.1 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1337
316ZendXmlRpcClient
316.1 Introduction . . . . . . .
316.2 Method Calls . . . . . . .
316.3 Types and Conversions . .
316.4 Server Proxy Object . . .
316.5 Error Handling . . . . . .
316.6 Server Introspection . . .
316.7 From Request to Response
316.8 HTTP Client and Testing .

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

1339
1339
1339
1340
1342
1342
1343
1344
1344

317ZendXmlRpcServer
317.1 Introduction . . . . . . . . . . . . . . . . . .
317.2 Basic Usage . . . . . . . . . . . . . . . . . .
317.3 Server Structure . . . . . . . . . . . . . . . .
317.4 Anatomy of a webservice . . . . . . . . . . .
317.5 Conventions . . . . . . . . . . . . . . . . . .
317.6 Utilizing Namespaces . . . . . . . . . . . . .
317.7 Custom Request Objects . . . . . . . . . . . .
317.8 Custom Responses . . . . . . . . . . . . . . .
317.9 Handling Exceptions via Faults . . . . . . . .
317.10Caching Server Definitions Between Requests
317.11Usage Examples . . . . . . . . . . . . . . . .
317.12Performance optimization . . . . . . . . . . .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1347
1347
1347
1347
1348
1348
1349
1349
1350
1350
1350
1351
1355

318ZendServiceAkismet
318.1 Introduction . . . . . . . . . .
318.2 Verify an API key . . . . . . .
318.3 Check for spam . . . . . . . . .
318.4 Submitting known spam . . . .
318.5 Submitting false positives (ham)
318.6 Zend-specific Methods . . . . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

1357
1357
1357
1358
1358
1359
1359

xxx

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
319ZendServiceAmazon
319.1 Introduction . . . . . . . . . . . . . . . . . .
319.2 Country Codes . . . . . . . . . . . . . . . . .
319.3 Looking up a Specific Amazon Item by ASIN .
319.4 Performing Amazon Item Searches . . . . . .
319.5 Using the Alternative Query API . . . . . . .
319.6 ZendServiceAmazon Classes . . . . . . . . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

1361
1361
1362
1362
1363
1363
1364

320ZendServiceAppleApns
320.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
320.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
320.3 Feedback Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1369
. 1369
. 1369
. 1371

321ZendServiceAudioscrobbler
321.1 Introduction . . . . . .
321.2 Users . . . . . . . . . .
321.3 Artists . . . . . . . . .
321.4 Tracks . . . . . . . . .
321.5 Tags . . . . . . . . . . .
321.6 Groups . . . . . . . . .
321.7 Forums . . . . . . . . .

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

1373
1373
1373
1375
1376
1376
1376
1377

322ZendServiceDelicious
322.1 Introduction . . . . . . . . . .
322.2 Retrieving posts . . . . . . . .
322.3 ZendServiceDeliciousPostList
322.4 Editing posts . . . . . . . . . .
322.5 Deleting posts . . . . . . . . .
322.6 Adding new posts . . . . . . .
322.7 Tags . . . . . . . . . . . . . . .
322.8 Bundles . . . . . . . . . . . . .
322.9 Public data . . . . . . . . . . .
322.10HTTP client . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

1379
1379
1379
1380
1381
1382
1382
1382
1383
1383
1384

323ZendServiceDeveloperGardenDeveloperGarden
323.1 Introduction to DeveloperGarden . . . . . .
323.2 BaseUserService . . . . . . . . . . . . . . .
323.3 IP Location . . . . . . . . . . . . . . . . . .
323.4 Local Search . . . . . . . . . . . . . . . . .
323.5 Send SMS . . . . . . . . . . . . . . . . . .
323.6 SMS Validation . . . . . . . . . . . . . . . .
323.7 Voice Call . . . . . . . . . . . . . . . . . .
323.8 ConferenceCall . . . . . . . . . . . . . . . .
323.9 Performance and Caching . . . . . . . . . .

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

1385
1385
1386
1387
1388
1388
1389
1389
1390
1392

324ZendServiceFlickr
324.1 Introduction . . . . . . . . . . . . . . . . .
324.2 Finding Flickr Users’ Photos and Information
324.3 Finding photos From a Group Pool . . . . .
324.4 Retrieving Flickr Image Details . . . . . . .
324.5 ZendServiceFlickr Result Classes . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

1393
1393
1393
1394
1394
1395

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

325ZendServiceGoogleGcm
1397
325.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1397
325.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1397

xxxi
326ZendServiceLiveDocxLiveDocx
1399
326.1 Introduction to LiveDocx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1399
326.2 ZendServiceLiveDocxMailMerge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1401
327ZendServiceNirvanix
327.1 Introduction . . . . . . .
327.2 Registering with Nirvanix
327.3 API Documentation . . .
327.4 Features . . . . . . . . . .
327.5 Getting Started . . . . . .
327.6 Understanding the Proxy .
327.7 Examining Results . . . .
327.8 Handling Errors . . . . .

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

1419
1419
1419
1419
1419
1420
1420
1421
1422

328ZendServiceRackspace
328.1 Introduction . . . . . . . .
328.2 Registering with Rackspace
328.3 Cloud Files . . . . . . . . .
328.4 Cloud Servers . . . . . . .
328.5 Available Methods . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

1423
1423
1423
1423
1424
1424

329ZendServiceReCaptcha
1427
329.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1427
329.2 Simplest use . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1427
329.3 Hiding email addresses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1428
330ZendServiceSlideShare
330.1 Getting Started with ZendServiceSlideShare
330.2 The SlideShow object . . . . . . . . . . . .
330.3 Retrieving a single slide show . . . . . . . .
330.4 Retrieving Groups of Slide Shows . . . . . .
330.5 ZendServiceSlideShare Caching policies . .
330.6 Changing the behavior of the HTTP Client .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

1431
1431
1431
1434
1434
1435
1435

331ZendServiceStrikeIron
331.1 Overview . . . . . . . . . .
331.2 Registering with StrikeIron
331.3 Getting Started . . . . . . .
331.4 Making Your First Query . .
331.5 Examining Results . . . . .
331.6 Handling Errors . . . . . .
331.7 Checking Your Subscription

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

1437
1437
1438
1438
1438
1439
1440
1440

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

332ZendServiceStrikeIron: Bundled Services
1443
332.1 ZIP Code Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1443
332.2 U.S. Address Verification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1444
332.3 Sales & Use Tax Basic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1445
333ZendServiceStrikeIron: Advanced Uses
1447
333.1 Using Services by WSDL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1447
333.2 Viewing SOAP Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1448
334ZendServiceTechnorati
1449
334.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1449
334.2 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1449

xxxii
334.3
334.4
334.5
334.6
334.7
334.8

Making Your First Query . . . . . . .
Consuming Results . . . . . . . . . .
Handling Errors . . . . . . . . . . .
Checking Your API Key Daily Usage
Available Technorati Queries . . . .
ZendServiceTechnorati Classes . . .

335ZendServiceTwitter
335.1 Introduction . . . . . .
335.2 Quick Start . . . . . . .
335.3 Authentication . . . . .
335.4 Account Methods . . . .
335.5 Application Methods . .
335.6 Blocking Methods . . .
335.7 Direct Message Methods
335.8 Favorites Methods . . .
335.9 Friendship Methods . .
335.10Search Methods . . . .
335.11Status Methods . . . . .
335.12User Methods . . . . . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

1449
1450
1452
1452
1453
1456

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1461
1461
1461
1463
1464
1465
1465
1466
1467
1467
1468
1468
1470

336ZendServiceWindowsAzure
336.1 Introduction . . . . . . . . . . . .
336.2 Installing the Windows Azure SDK
336.3 API Documentation . . . . . . . .
336.4 Features . . . . . . . . . . . . . . .
336.5 Architecture . . . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

1473
1473
1473
1473
1473
1474

337ZendServiceWindowsAzureStorageBlob
337.1 API Examples . . . . . . . . . . . . .
337.2 Root container . . . . . . . . . . . . .
337.3 Blob storage stream wrapper . . . . . .
337.4 Shared Access Signature . . . . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

1475
1475
1477
1477
1478

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

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

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

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

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

338ZendServiceWindowsAzureStorageTable
1481
338.1 Operations on tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1481
338.2 Operations on entities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1482
338.3 Table storage session handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1488
339ZendServiceWindowsAzureStorageQueue
1491
339.1 API Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1491
340Copyright Information

1495

341Introduction to Zend Framework 2

1497

342User Guide

1499

343Zend Framework Tool (ZFTool)

1501

344Learning Zend Framework 2

1503

345Migration

1505

346Zend Framework 2 Reference
1507
346.1 ZendAuthentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1507

xxxiii
346.2 ZendBarcode . . . . .
346.3 ZendCache . . . . . . .
346.4 ZendCaptcha . . . . . .
346.5 ZendConfig . . . . . .
346.6 ZendConsole . . . . . .
346.7 ZendCrypt . . . . . . .
346.8 ZendDb . . . . . . . .
346.9 ZendDi . . . . . . . . .
346.10ZendDom . . . . . . .
346.11ZendEscaper . . . . . .
346.12ZendEventManager . .
346.13ZendFeed . . . . . . .
346.14ZendFile . . . . . . . .
346.15ZendFilter . . . . . . .
346.16ZendForm . . . . . . .
346.17ZendHttp . . . . . . . .
346.18ZendI18n . . . . . . .
346.19ZendInputFilter . . . .
346.20ZendJson . . . . . . . .
346.21ZendLdap . . . . . . .
346.22ZendLoader . . . . . .
346.23ZendLog . . . . . . . .
346.24ZendMail . . . . . . .
346.25ZendMath . . . . . . .
346.26ZendMime . . . . . . .
346.27ZendModuleManager .
346.28ZendMvc . . . . . . .
346.29ZendNavigation . . . .
346.30ZendPaginator . . . . .
346.31ZendPermissionsAcl .
346.32ZendPermissionsRbac
346.33ZendProgressBar . . .
346.34ZendSerializer . . . . .
346.35ZendServer . . . . . .
346.36ZendServiceManager .
346.37ZendSession . . . . . .
346.38ZendSoap . . . . . . .
346.39ZendStdlib . . . . . . .
346.40ZendTag . . . . . . . .
346.41ZendTest . . . . . . . .
346.42ZendText . . . . . . . .
346.43ZendUri . . . . . . . .
346.44ZendValidator . . . . .
346.45ZendVersion . . . . . .
346.46ZendView . . . . . . .
346.47ZendXmlRpc . . . . .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1507
1507
1508
1508
1508
1508
1509
1509
1509
1509
1510
1510
1510
1510
1510
1511
1511
1511
1511
1512
1512
1512
1512
1513
1513
1513
1513
1513
1514
1514
1514
1514
1514
1515
1515
1515
1515
1515
1516
1516
1516
1516
1516
1516
1516
1517

347Services for Zend Framework 2 Reference
347.1 ZendServiceAkismet . . . . . . . . .
347.2 ZendServiceAmazon . . . . . . . . .
347.3 ZendServiceAppleApns . . . . . . . .
347.4 ZendServiceAudioscrobbler . . . . . .
347.5 ZendServiceDel.icio.us . . . . . . . .
347.6 ZendServiceDeveloper Garden . . . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

1519
1519
1519
1519
1519
1519
1519

xxxiv

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

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

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

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

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

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

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
347.7 ZendServiceFlickr . . . . . .
347.8 ZendServiceGoogleGcm . .
347.9 ZendServiceLiveDocx . . . .
347.10ZendServiceNirvanix . . . .
347.11ZendServiceRackspace . . .
347.12ZendServiceReCaptcha . . .
347.13ZendServiceSlideShare . . .
347.14ZendServiceStrikeIron . . . .
347.15ZendServiceTechnorati . . .
347.16ZendServiceTwitter . . . . .
347.17ZendServiceWindows Azure

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

1520
1520
1520
1520
1520
1520
1520
1520
1520
1521
1521

348Copyright

1523

349Indices and tables

1525

xxxv
xxxvi
CHAPTER 1

Overview

Zend Framework 2 is an open source framework for developing web applications and services using PHP 5.3+. Zend
Framework 2 uses 100% object-oriented code and utilises most of the new features of PHP 5.3, namely namespaces,
late static binding, lambda functions and closures.
Zend Framework 2 evolved from Zend Framework 1, a successful PHP framework with over 15 million downloads.
Note: ZF2 is not backward compatible with ZF1, because of the new features in PHP 5.3+ implemented by the
framework, and due to major rewrites of many components.
The component structure of Zend Framework 2 is unique; each component is designed with few dependencies on
other components. ZF2 follows the SOLID object-oriented design principle. This loosely coupled architecture allows
developers to use whichever components they want. We call this a “use-at-will” design. We support Pyrus and
Composer as installation and dependency tracking mechanisms for the framework as a whole and for each component,
further enhancing this design.
We use PHPUnit to test our code and Travis CI as a Continuous Integration service.
While they can be used separately, Zend Framework 2 components in the standard library form a powerful and extensible web application framework when combined. Also, it offers a robust, high performance MVC implementation,
a database abstraction that is simple to use, and a forms component that implements HTML5 form rendering, validation, and filtering so that developers can consolidate all of these operations using one easy-to-use, object oriented
interface. Other components, such as ZendAuthentication and ZendPermissionsAcl, provide user
authentication and authorization against all common credential stores.
Still others, with the ZendService namespace, implement client libraries to simply access the most popular web
services available. Whatever your application needs are, you’re likely to find a Zend Framework 2 component that can
be used to dramatically reduce development time with a thoroughly tested foundation.
The principal sponsor of the project ‘Zend Framework 2’ is Zend Technologies, but many companies have contributed
components or significant features to the framework. Companies such as Google, Microsoft, and StrikeIron have
partnered with Zend to provide interfaces to web services and other technologies they wish to make available to Zend
Framework 2 developers.
Zend Framework 2 could not deliver and support all of these features without the help of the vibrant Zend Framework
2 community. Community members, including contributors, make themselves available on mailing lists, IRC channels
and other forums. Whatever question you have about Zend Framework 2, the community is always available to address
it.

1
Zend Framework 2 Documentation, Release 2.2.5

2

Chapter 1. Overview
CHAPTER 2

Installation

• New to Zend Framework? Download the latest stable release. Available in .zip and .tar.gz formats.
• Brave, cutting edge? Download Zend Framework’s Git repository using a Git client. Zend Framework is open
source software, and the Git repository used for its development is publicly available on GitHub. Consider using
Git to get Zend Framework if you want to contribute back to the framework, or need to upgrade your framework
version more often than releases occur.
Once you have a copy of Zend Framework available, your application needs to be able to access the framework classes
found in the library folder. There are several ways to achieve this.
Failing to find a Zend Framework 2 installation, the following error occurs:
Fatal error: Uncaught exception ’RuntimeException’ with message
’Unable to load ZF2. Run ‘php composer.phar install‘ or define
a ZF2_PATH environment variable.’

To fix that, you can add the Zend Framework’s library path to the PHP include_path. Also, you should set an environment path named ‘ZF2_PATH’ in httpd.conf (or equivalent). i.e. SetEnv ZF2_PATH /var/ZF2 running
Linux.
Rob Allen has kindly provided the community with an introductory tutorial, Getting Started with Zend Framework 2.
Other Zend Framework community members are actively working on expanding the tutorial.

3
Zend Framework 2 Documentation, Release 2.2.5

4

Chapter 2. Installation
CHAPTER 3

Getting Started with Zend Framework 2

This tutorial is intended to give an introduction to using Zend Framework 2 by creating a simple database driven
application using the Model-View-Controller paradigm. By the end you will have a working ZF2 application and you
can then poke around the code to find out more about how it all works and fits together.

3.1 Some assumptions
This tutorial assumes that you are running at least PHP 5.3.3 with the Apache web server and MySQL, accessible via
the PDO extension. Your Apache installation must have the mod_rewrite extension installed and configured.
You must also ensure that Apache is configured to support .htaccess files. This is usually done by changing the
setting:
1

AllowOverride None

to
1

AllowOverride FileInfo

in your httpd.conf file. Check with your distributions documentation for exact details. You will not be able to
navigate to any page other than the home page in this tutorial if you have not configured mod_rewrite and .htaccess
usage correctly.

3.2 The tutorial application
The application that we are going to build is a simple inventory system to display which albums we own. The main
page will list our collection and allow us to add, edit and delete CDs. We are going to need four pages in our website:
Page
List of
albums
Add new
album
Edit album
Delete
album

Description
This will display the list of albums and provide links to edit and delete them. Also, a link to enable
adding new albums will be provided.
This page will provide a form for adding a new album.
This page will provide a form for editing an album.
This page will confirm that we want to delete an album and then delete it.

5
Zend Framework 2 Documentation, Release 2.2.5

We will also need to store our data into a database. We will only need one table with these fields in it:
Field name
id
artist
title

6

Type
integer
varchar(100)
varchar(100)

Null?
No
No
No

Notes
Primary key, auto-increment

Chapter 3. Getting Started with Zend Framework 2
CHAPTER 4

Getting started: A skeleton application

In order to build our application, we will start with the ZendSkeletonApplication available on github. Use Composer
(http://getcomposer.org) to create a new project from scratch with Zend Framework:
1
2

php composer.phar create-project --repository-url="https://packages.zendframework.com" -s dev zendfr
php composer.phar update

Note:
Another way to install the ZendSkeletonApplication is to use github.
Go to
https://github.com/zendframework/ZendSkeletonApplication and click the Zip button. This will download a
file with a name like ZendSkeletonApplication-master.zip or similar.
Unzip this file into the directory where you keep all your vhosts and rename the resultant directory to
zf2-tutorial.
ZendSkeletonApplication is set up to use Composer (http://getcomposer.org) to resolve its dependencies. In this case,
the dependency is Zend Framework 2 itself.
To install Zend Framework 2 into our application we simply type:
1
2
3

php composer.phar self-update
php composer.phar install
php composer.phar update

from the zf2-tutorial folder. This takes a while. You should see an output like:
1
2
3

Installing dependencies from lock file
- Installing zendframework/zendframework (dev-master)
Cloning 18c8e223f070deb07c17543ed938b54542aa0ed8

4
5

Generating autoload files

Note: If you see this message:
1
2

[RuntimeException]
The process timed out.

then your connection was too slow to download the entire package in time, and composer timed out. To avoid this,
instead of running:

7
Zend Framework 2 Documentation, Release 2.2.5

1
2

php composer.phar install
php composer.phar update

run instead:
1
2

COMPOSER_PROCESS_TIMEOUT=5000 php composer.phar install
COMPOSER_PROCESS_TIMEOUT=5000 php composer.phar update

We can now move on to the virtual host.

4.1 Using the Apache Web Server
You now need to create an Apache virtual host for the application and edit your hosts file so that http://zf2tutorial.localhost will serve index.php from the zf2-tutorial/public directory.
Setting up the virtual host is usually done within httpd.conf or extra/httpd-vhosts.conf. If you are using
httpd-vhosts.conf, ensure that this file is included by your main httpd.conf file. Some Linux distributions
(ex: Ubuntu) package Apache so that configuration files are stored in /etc/apache2 and create one file per virtual
host inside folder /etc/apache2/sites-enabled. In this case, you would place the virtual host block below
into the file /etc/apache2/sites-enabled/zf2-tutorial.
Ensure that NameVirtualHost is defined and set to *:80 or similar, and then define a virtual host along these lines:
1
2
3
4
5
6
7
8
9
10
11

<VirtualHost *:80>
ServerName zf2-tutorial.localhost
DocumentRoot /path/to/zf2-tutorial/public
SetEnv APPLICATION_ENV "development"
<Directory /path/to/zf2-tutorial/public>
DirectoryIndex index.php
AllowOverride All
Order allow,deny
Allow from all
</Directory>
</VirtualHost>

Make sure that you update your /etc/hosts or c:windowssystem32driversetchosts file so that
zf2-tutorial.localhost is mapped to 127.0.0.1. The website can then be accessed using http://zf2tutorial.localhost.
127.0.0.1

zf2-tutorial.localhost localhost

Restart your web server. If youve done it right, you should see something like this:

8

Chapter 4. Getting started: A skeleton application
Zend Framework 2 Documentation, Release 2.2.5

To test that your .htaccess file is working, navigate to http://zf2-tutorial.localhost/1234 and you should see this:

If you see a standard Apache 404 error, then you need to fix .htaccess usage before continuing. If you’re are using
IIS with the URL Rewrite Module, import the following:
1
2

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^.*$ index.php [NC,L]

You now have a working skeleton application and we can start adding the specifics for our application.

4.2 Error reporting
Optionally, you can use the APPLICATION_ENV setting in your virtualhost to let PHP output all its errors to the
browser. This can be useful when during development of your application.
Edit index.php from the zf2-tutorial/public/ directory and change it to the following:

4.2. Error reporting

9
Zend Framework 2 Documentation, Release 2.2.5

1

<?php

2
3
4
5
6
7
8
9

/**
* Display all errors when APPLICATION_ENV is development.
*/
if ($_SERVER[’APPLICATION_ENV’] == ’development’) {
error_reporting(E_ALL);
ini_set("display_errors", 1);
}

10
11
12
13
14
15

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

16
17
18

// Setup autoloading
require ’init_autoloader.php’;

19
20
21

// Run the application!
ZendMvcApplication::init(require ’config/application.config.php’)->run();

10

Chapter 4. Getting started: A skeleton application
CHAPTER 5

Modules

Zend Framework 2 uses a module system and you organise your main application-specific code within each module.
The Application module provided by the skeleton is used to provide bootstrapping, error and routing configuration
to the whole application. It is usually used to provide application level controllers for, say, the home page of an
application, but we are not going to use the default one provided in this tutorial as we want our album list to be the
home page, which will live in our own module.
We are going to put all our code into the Album module which will contain our controllers, models, forms and views,
along with configuration. Well also tweak the Application module as required.
Lets start with the directories required.

5.1 Setting up the Album module
Start by creating a directory called Album under module with the following subdirectories to hold the modules files:
1
2
3
4
5
6
7
8
9
10
11
12

zf2-tutorial/
/module
/Album
/config
/src
/Album
/Controller
/Form
/Model
/view
/album
/album

As you can see the Album module has separate directories for the different types of files we will have. The PHP
files that contain classes within the Album namespace live in the src/Album directory so that we can have multiple
namespaces within our module should we require it. The view directory also has a sub-folder called album for our
modules view scripts.
In order to load and configure a module, Zend Framework 2 has a ModuleManager. This will look
for Module.php in the root of the module directory (module/Album) and expect to find a class called
AlbumModule within it. That is, the classes within a given module will have the namespace of the modules
name, which is the directory name of the module.

11
Zend Framework 2 Documentation, Release 2.2.5

Create Module.php in the Album
zf2-tutorial/module/Album:
1

module:

Create

a

file

called

Module.php

under

namespace Album;

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

class Module
{
public function getAutoloaderConfig()
{
return array(
’ZendLoaderClassMapAutoloader’ => array(
__DIR__ . ’/autoload_classmap.php’,
),
’ZendLoaderStandardAutoloader’ => array(
’namespaces’ => array(
__NAMESPACE__ => __DIR__ . ’/src/’ . __NAMESPACE__,
),
),
);
}

18

public function getConfig()
{
return include __DIR__ . ’/config/module.config.php’;
}

19
20
21
22
23

}

The ModuleManager will call getAutoloaderConfig() and getConfig() automatically for us.

5.1.1 Autoloading files
Our getAutoloaderConfig() method returns an array that is compatible with ZF2s AutoloaderFactory.
We configure it so that we add a class map file to the ClassMapAutoloader and also add this modules namespace
to the StandardAutoloader. The standard autoloader requires a namespace and the path where to find the files
for that namespace. It is PSR-0 compliant and so classes map directly to files as per the PSR-0 rules.
As we are in development, we dont need to load files via the classmap, so we provide an empty array for the classmap
autoloader. Create a file called autoload_classmap.php under zf2-tutorial/module/Album:
1

return array();

As this is an empty array, whenever the autoloader looks for a class within the Album namespace, it will fall back to
the to StandardAutoloader for us.
Note: If you are using Composer, you could instead just create an empty getAutoloaderConfig() { } and
add to composer.json:
1
2
3

"autoload": {
"psr-0": { "Album": "module/Album/src/" }
},

If you go this way, then you need to run php composer.phar update to update the composer autoloading files.

12

Chapter 5. Modules
Zend Framework 2 Documentation, Release 2.2.5

5.2 Configuration
Having registered the autoloader, lets have a quick look at the getConfig() method in AlbumModule. This
method simply loads the config/module.config.php file.
Create a file called module.config.php under zf2-tutorial/module/Album/config:
1
2
3
4
5
6
7
8
9
10
11
12

return array(
’controllers’ => array(
’invokables’ => array(
’AlbumControllerAlbum’ => ’AlbumControllerAlbumController’,
),
),
’view_manager’ => array(
’template_path_stack’ => array(
’album’ => __DIR__ . ’/../view’,
),
),
);

The config information is passed to the relevant components by the ServiceManager. We need two initial sections: controllers and view_manager. The controllers section provides a list of all the controllers provided by the module. We will need one controller, AlbumController, which well reference as
AlbumControllerAlbum. The controller key must be unique across all modules, so we prefix it with our
module name.
Within the view_manager section, we add our view directory to the TemplatePathStack configuration. This
will allow it to find the view scripts for the Album module that are stored in our view/ directory.

5.3 Informing the application about our new module
We now need to tell the ModuleManager that this new module exists. This is done in the applications
config/application.config.php file which is provided by the skeleton application. Update this file so
that its modules section contains the Album module as well, so the file now looks like this:
(Changes required are highlighted using comments.)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

return array(
’modules’ => array(
’Application’,
’Album’,
// <-- Add this line
),
’module_listener_options’ => array(
’config_glob_paths’
=> array(
’config/autoload/{,*.}{global,local}.php’,
),
’module_paths’ => array(
’./module’,
’./vendor’,
),
),
);

As you can see, we have added our Album module into the list of modules after the Application module.
We have now set up the module ready for putting our custom code into it.

5.2. Configuration

13
Zend Framework 2 Documentation, Release 2.2.5

14

Chapter 5. Modules
CHAPTER 6

Routing and controllers

We will build a very simple inventory system to display our album collection. The home page will list our collection
and allow us to add, edit and delete albums. Hence the following pages are required:
Page
Home
Add new
album
Edit album
Delete
album

Description
This will display the list of albums and provide links to edit and delete them. Also, a link to enable
adding new albums will be provided.
This page will provide a form for adding a new album.
This page will provide a form for editing an album.
This page will confirm that we want to delete an album and then delete it.

Before we set up our files, its important to understand how the framework expects the pages to be organised. Each page
of the application is known as an action and actions are grouped into controllers within modules. Hence, you would
generally group related actions into a controller; for instance, a news controller might have actions of current,
archived and view.
As we have four pages that all apply to albums, we will group them in a single controller AlbumController within
our Album module as four actions. The four actions will be:
Page
Home
Add new album
Edit album
Delete album

Controller
AlbumController
AlbumController
AlbumController
AlbumController

Action
index
add
edit
delete

The mapping of a URL to a particular action is done using routes that are defined in the modules
module.config.php file. We will add a route for our album actions. This is the updated module config file
with the new code highlighted.
1
2
3
4
5
6

return array(
’controllers’ => array(
’invokables’ => array(
’AlbumControllerAlbum’ => ’AlbumControllerAlbumController’,
),
),

7
8
9
10

// The following section is new and should be added to your file
’router’ => array(
’routes’ => array(

15
Zend Framework 2 Documentation, Release 2.2.5

’album’ => array(
’type’
=> ’segment’,
’options’ => array(
’route’
=> ’/album[/][:action][/:id]’,
’constraints’ => array(
’action’ => ’[a-zA-Z][a-zA-Z0-9_-]*’,
’id’
=> ’[0-9]+’,
),
’defaults’ => array(
’controller’ => ’AlbumControllerAlbum’,
’action’
=> ’index’,
),
),
),

11
12
13
14
15
16
17
18
19
20
21
22
23
24

),

25

),

26
27

’view_manager’ => array(
’template_path_stack’ => array(
’album’ => __DIR__ . ’/../view’,
),
),

28
29
30
31
32
33

);

The name of the route is album and has a type of segment. The segment route allows us to specify placeholders in
the URL pattern (route) that will be mapped to named parameters in the matched route. In this case, the route is
‘‘/album[/:action][/:id]‘‘ which will match any URL that starts with /album. The next segment will be an optional
action name, and then finally the next segment will be mapped to an optional id. The square brackets indicate that a
segment is optional. The constraints section allows us to ensure that the characters within a segment are as expected,
so we have limited actions to starting with a letter and then subsequent characters only being alphanumeric, underscore
or hyphen. We also limit the id to a number.
This route allows us to have the following URLs:
URL
/album
/album/add
/album/edit/2
/album/delete/4

16

Page
Home (list of albums)
Add new album
Edit album with an id of 2
Delete album with an id of 4

Action
index
add
edit
delete

Chapter 6. Routing and controllers
CHAPTER 7

Create the controller

We are now ready to set up our controller. In Zend Framework 2, the controller is a class that is generally called
{Controller name}Controller. Note that {Controller name} must start with a capital letter. This
class lives in a file called {Controller name}Controller.php within the Controller directory for the
module. In our case that is module/Album/src/Album/Controller. Each action is a public method within
the controller class that is named {action name}Action. In this case {action name} should start with a
lower case letter.
Note:
This is by convention. Zend Framework 2 doesnt provide many restrictions on controllers other
than that they must implement the ZendStdlibDispatchable interface. The framework provides
two abstract classes that do this for us: ZendMvcControllerAbstractActionController
and ZendMvcControllerAbstractRestfulController.
Well be using the standard AbstractActionController, but if youre intending to write a RESTful web service,
AbstractRestfulController may be useful.
Lets
go
ahead
and
create
our
controller
class
zf2-tutorials/module/Album/src/Album/Controller :
1

AlbumController.php

at

namespace AlbumController;

2
3
4

use ZendMvcControllerAbstractActionController;
use ZendViewModelViewModel;

5
6
7
8
9
10

class AlbumController extends AbstractActionController
{
public function indexAction()
{
}

11
12
13
14

public function addAction()
{
}

15
16
17
18

public function editAction()
{
}

19
20
21

public function deleteAction()
{

17
Zend Framework 2 Documentation, Release 2.2.5

}

22
23

}

Note:
We have already informed the module about our controller in the controller section of
module/Album/config/module.config.php.
We have now set up the four actions that we want to use. They wont work yet until we set up the views. The URLs for
each action are:
URL
http://zf2-tutorial.localhost/album
http://zf2-tutorial.localhost/album/add
http://zf2-tutorial.localhost/album/edit
http://zf2-tutorial.localhost/album/delete

Method called
AlbumControllerAlbumController::indexAction
AlbumControllerAlbumController::addAction
AlbumControllerAlbumController::editAction
AlbumControllerAlbumController::deleteAction

We now have a working router and the actions are set up for each page of our application.
Its time to build the view and the model layer.

7.1 Initialise the view scripts
To integrate the view into our application all we need to do is create some view script files. These files will be
executed by the DefaultViewStrategy and will be passed any variables or view models that are returned from
the controller action method. These view scripts are stored in our modules views directory within a directory named
after the controller. Create these four empty files now:
• module/Album/view/album/album/index.phtml
• module/Album/view/album/album/add.phtml
• module/Album/view/album/album/edit.phtml
• module/Album/view/album/album/delete.phtml
We can now start filling everything in, starting with our database and models.

18

Chapter 7. Create the controller
CHAPTER 8

Database and models

8.1 The database
Now that we have the Album module set up with controller action methods and view scripts, it is time to look at the
model section of our application. Remember that the model is the part that deals with the applications core purpose
(the so-called business rules) and, in our case, deals with the database. We will make use of the Zend Framework class
ZendDbTableGatewayTableGateway which is used to find, insert, update and delete rows from a database
table.
We are going to use MySQL, via PHPs PDO driver, so create a database called zf2tutorial, and run these SQL
statements to create the album table with some data in it.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

CREATE TABLE album (
id int(11) NOT NULL auto_increment,
artist varchar(100) NOT NULL,
title varchar(100) NOT NULL,
PRIMARY KEY (id)
);
INSERT INTO album (artist, title)
VALUES (’The Military Wives’, ’In My Dreams’);
INSERT INTO album (artist, title)
VALUES (’Adele’, ’21’);
INSERT INTO album (artist, title)
VALUES (’Bruce Springsteen’, ’Wrecking Ball (Deluxe)’);
INSERT INTO album (artist, title)
VALUES (’Lana Del Rey’, ’Born To Die’);
INSERT INTO album (artist, title)
VALUES (’Gotye’, ’Making Mirrors’);

(The test data chosen happens to be the Bestsellers on Amazon UK at the time of writing!)
We now have some data in a database and can write a very simple model for it.

8.2 The model files
Zend Framework does not provide a ZendModel component because the model is your business logic and its up
to you to decide how you want it to work. There are many components that you can use for this depending on your
needs. One approach is to have model classes represent each entity in your application and then use mapper objects
19
Zend Framework 2 Documentation, Release 2.2.5

that load and save entities to the database. Another is to use an Object-relational mapping (ORM) technology, such as
Doctrine or Propel.
For this tutorial, we are going to create a very simple model by creating an AlbumTable class that uses the
ZendDbTableGatewayTableGateway class in which each album object is an Album object (known as
an entity). This is an implementation of the Table Data Gateway design pattern to allow for interfacing with data
in a database table. Be aware though that the Table Data Gateway pattern can become limiting in larger systems. There is also a temptation to put database access code into controller action methods as these are exposed
by ZendDbTableGatewayAbstractTableGateway. Dont do this!
Lets start by creating a file called Album.php under module/Album/src/Album/Model:
1

namespace AlbumModel;

2
3
4
5
6
7

class Album
{
public $id;
public $artist;
public $title;

8

public function exchangeArray($data)
{
$this->id
= (!empty($data[’id’])) ? $data[’id’] : null;
$this->artist = (!empty($data[’artist’])) ? $data[’artist’] : null;
$this->title = (!empty($data[’title’])) ? $data[’title’] : null;
}

9
10
11
12
13
14
15

}

Our Album entity object is a simple PHP class. In order to work with ZendDbs TableGateway class, we need
to implement the exchangeArray() method. This method simply copies the data from the passed in array to our
entitys properties. We will add an input filter for use with our form later.
Next, we create our AlbumTable.php file in module/Album/src/Album/Model directory like this:
1

namespace AlbumModel;

2
3

use ZendDbTableGatewayTableGateway;

4
5
6
7

class AlbumTable
{
protected $tableGateway;

8

public function __construct(TableGateway $tableGateway)
{
$this->tableGateway = $tableGateway;
}

9
10
11
12
13

public function fetchAll()
{
$resultSet = $this->tableGateway->select();
return $resultSet;
}

14
15
16
17
18
19

public function getAlbum($id)
{
$id = (int) $id;
$rowset = $this->tableGateway->select(array(’id’ => $id));
$row = $rowset->current();
if (!$row) {
throw new Exception("Could not find row $id");

20
21
22
23
24
25
26

20

Chapter 8. Database and models
Zend Framework 2 Documentation, Release 2.2.5

}
return $row;

27
28

}

29
30

public function saveAlbum(Album $album)
{
$data = array(
’artist’ => $album->artist,
’title’ => $album->title,
);

31
32
33
34
35
36
37

$id = (int) $album->id;
if ($id == 0) {
$this->tableGateway->insert($data);
} else {
if ($this->getAlbum($id)) {
$this->tableGateway->update($data, array(’id’ => $id));
} else {
throw new Exception(’Album id does not exist’);
}
}

38
39
40
41
42
43
44
45
46
47

}

48
49

public function deleteAlbum($id)
{
$this->tableGateway->delete(array(’id’ => (int) $id));
}

50
51
52
53
54

}

Theres a lot going on here. Firstly, we set the protected property $tableGateway to the TableGateway instance
passed in the constructor. We will use this to perform operations on the database table for our albums.
We then create some helper methods that our application will use to interface with the table gateway. fetchAll() retrieves all albums rows from the database as a ResultSet, getAlbum() retrieves a single row as an Album object,
saveAlbum() either creates a new row in the database or updates a row that already exists and deleteAlbum()
removes the row completely. The code for each of these methods is, hopefully, self-explanatory.

8.3 Using ServiceManager to configure the table gateway and inject
into the AlbumTable
In order to always use the same instance of our AlbumTable, we will use the ServiceManager to define how to
create one. This is most easily done in the Module class where we create a method called getServiceConfig()
which is automatically called by the ModuleManager and applied to the ServiceManager. Well then be able to
retrieve it in our controller when we need it.
To configure the ServiceManager, we can either supply the name of the class to be instantiated or a factory
(closure or callback) that instantiates the object when the ServiceManager needs it. We start by implementing
getServiceConfig() to provide a factory that creates an AlbumTable. Add this method to the bottom of the
Module.php file in module/Album.
1

namespace Album;

2
3
4
5
6

// Add these import statements:
use AlbumModelAlbum;
use AlbumModelAlbumTable;
use ZendDbResultSetResultSet;

8.3. Using ServiceManager to configure the table gateway and inject into the AlbumTable

21
Zend Framework 2 Documentation, Release 2.2.5

7

use ZendDbTableGatewayTableGateway;

8
9
10
11

class Module
{
// getAutoloaderConfig() and getConfig() methods here

12

// Add this method:
public function getServiceConfig()
{
return array(
’factories’ => array(
’AlbumModelAlbumTable’ => function($sm) {
$tableGateway = $sm->get(’AlbumTableGateway’);
$table = new AlbumTable($tableGateway);
return $table;
},
’AlbumTableGateway’ => function ($sm) {
$dbAdapter = $sm->get(’ZendDbAdapterAdapter’);
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Album());
return new TableGateway(’album’, $dbAdapter, null, $resultSetPrototype);
},
),
);
}

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

}

This method returns an array of factories that are all merged together by the ModuleManager before passing to the ServiceManager. The factory for AlbumModelAlbumTable uses the ServiceManager
to create an AlbumTableGateway to pass to the AlbumTable. We also tell the ServiceManager
that an AlbumTableGateway is created by getting a ZendDbAdapterAdapter (also from the
ServiceManager) and using it to create a TableGateway object. The TableGateway is told to use an Album
object whenever it creates a new result row. The TableGateway classes use the prototype pattern for creation of result
sets and entities. This means that instead of instantiating when required, the system clones a previously instantiated
object. See PHP Constructor Best Practices and the Prototype Pattern for more details.
Finally, we need to configure the ServiceManager so that it knows how to get a ZendDbAdapterAdapter.
This is done using a factory called ZendDbAdapterAdapterServiceFactory which we can configure
within the merged config system. Zend Framework 2s ModuleManager merges all the configuration from each
modules module.config.php file and then merges in the files in config/autoload (*.global.php and
then *.local.php files). Well add our database configuration information to global.php which you should
commit to your version control system. You can use local.php (outside of the VCS) to store the credentials for
your database if you want to. Modify config/autoload/global.php (in the Zend Skeleton root, not inside
the Album module) with following code:
1
2
3
4
5
6
7
8
9
10
11
12
13

return array(
’db’ => array(
’driver’
=> ’Pdo’,
’dsn’
=> ’mysql:dbname=zf2tutorial;host=localhost’,
’driver_options’ => array(
PDO::MYSQL_ATTR_INIT_COMMAND => ’SET NAMES ’UTF8’’
),
),
’service_manager’ => array(
’factories’ => array(
’ZendDbAdapterAdapter’
=> ’ZendDbAdapterAdapterServiceFactory’,
),

22

Chapter 8. Database and models
Zend Framework 2 Documentation, Release 2.2.5

),

14
15

);

You should put your database credentials in config/autoload/local.php so that they are not in the git repository (as local.php is ignored):
1
2
3
4
5
6

return array(
’db’ => array(
’username’ => ’YOUR USERNAME HERE’,
’password’ => ’YOUR PASSWORD HERE’,
),
);

8.4 Back to the controller
Now that the ServiceManager can create an AlbumTable instance for us, we can add a method to the controller
to retrieve it. Add getAlbumTable() to the AlbumController class:
1
2
3
4
5
6
7
8
9

// module/Album/src/Album/Controller/AlbumController.php:
public function getAlbumTable()
{
if (!$this->albumTable) {
$sm = $this->getServiceLocator();
$this->albumTable = $sm->get(’AlbumModelAlbumTable’);
}
return $this->albumTable;
}

You should also add:
1

protected $albumTable;

to the top of the class.
We can now call getAlbumTable() from within our controller whenever we need to interact with our model.
If the service locator was configured correctly in Module.php, then we should get an instance of
AlbumModelAlbumTable when calling getAlbumTable().

8.5 Listing albums
In order to list the albums, we need to retrieve them from the model and pass them to the view. To do this, we fill in
indexAction() within AlbumController. Update the AlbumControllers indexAction() like this:
1
2
3
4
5
6
7
8
9

// module/Album/src/Album/Controller/AlbumController.php:
// ...
public function indexAction()
{
return new ViewModel(array(
’albums’ => $this->getAlbumTable()->fetchAll(),
));
}
// ...

8.4. Back to the controller

23
Zend Framework 2 Documentation, Release 2.2.5

With Zend Framework 2, in order to set variables in the view, we return a ViewModel instance where the first
parameter of the constructor is an array from the action containing data we need. These are then automatically passed
to the view script. The ViewModel object also allows us to change the view script that is used, but the default is to
use {controller name}/{action name}. We can now fill in the index.phtml view script:
1
2

<?php
// module/Album/view/album/album/index.phtml:

3
4
5
6
7
8
9
10

$title = ’My albums’;
$this->headTitle($title);
?>
<h1><?php echo $this->escapeHtml($title); ?></h1>
<p>
<a href="<?php echo $this->url(’album’, array(’action’=>’add’));?>">Add new album</a>
</p>

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

<table class="table">
<tr>
<th>Title</th>
<th>Artist</th>
<th>&nbsp;</th>
</tr>
<?php foreach ($albums as $album) : ?>
<tr>
<td><?php echo $this->escapeHtml($album->title);?></td>
<td><?php echo $this->escapeHtml($album->artist);?></td>
<td>
<a href="<?php echo $this->url(’album’,
array(’action’=>’edit’, ’id’ => $album->id));?>">Edit</a>
<a href="<?php echo $this->url(’album’,
array(’action’=>’delete’, ’id’ => $album->id));?>">Delete</a>
</td>
</tr>
<?php endforeach; ?>
</table>

The first thing we do is to set the title for the page (used in the layout) and also set the title for the <head> section
using the headTitle() view helper which will display in the browsers title bar. We then create a link to add a new
album.
The url() view helper is provided by Zend Framework 2 and is used to create the links we need. The first parameter
to url() is the route name we wish to use for construction of the URL, and the second parameter is an array of all
the variables to fit into the placeholders to use. In this case we use our album route which is set up to accept two
placeholder variables: action and id.
We iterate over the $albums that we assigned from the controller action. The Zend Framework 2 view system
automatically ensures that these variables are extracted into the scope of the view script, so that we dont have to worry
about prefixing them with $this-> as we used to have to do with Zend Framework 1; however you can do so if you
wish.
We then create a table to display each albums title and artist, and provide links to allow for editing and deleting the
record. A standard foreach: loop is used to iterate over the list of albums, and we use the alternate form using a
colon and endforeach; as it is easier to scan than to try and match up braces. Again, the url() view helper is
used to create the edit and delete links.
Note: We always use the escapeHtml() view helper to help protect ourselves from Cross Site Scripting (XSS)
vulnerabilities (see http://en.wikipedia.org/wiki/Cross-site_scripting).

24

Chapter 8. Database and models
Zend Framework 2 Documentation, Release 2.2.5

If you open http://zf2-tutorial.localhost/album you should see this:

8.5. Listing albums

25
Zend Framework 2 Documentation, Release 2.2.5

26

Chapter 8. Database and models
CHAPTER 9

Styling and Translations

Weve picked up the SkeletonApplications styling, which is fine, but we need to change the title and remove the
copyright message.
The ZendSkeletonApplication is set up to use ZendI18ns translation functionality for all the text. It uses .po files
that live in module/Application/language, and you need to use poedit to change the text. Start poedit and
open module/Application/language/en_US.po. Click on Skeleton Application in the list of Original
strings and then type in Tutorial as the translation.

Press Save in the toolbar and poedit will create an en_US.mo file for us. If you find that no .mo file is generated, check Preferences -> Editor -> Behavior and see if the checkbox marked Automatically
compile .mo file on save is checked.
To remove the copyright message, we need to edit the Application modules layout.phtml view script:
1
2
3
4

// module/Application/view/layout/layout.phtml:
// Remove this line:
<p>&copy; 2005 - 2013 by Zend Technologies Ltd. <?php echo $this->translate(’All
rights reserved.’) ?></p>

27
Zend Framework 2 Documentation, Release 2.2.5

The page now looks ever so slightly better now!

28

Chapter 9. Styling and Translations
CHAPTER 10

Forms and actions

10.1 Adding new albums
We can now code up the functionality to add new albums. There are two bits to this part:
• Display a form for user to provide details
• Process the form submission and store to database
We use ZendForm to do this. The ZendForm component manages the form and, form validation, we add
a ZendInputFilter to our Album entity. We start by creating a new class AlbumFormAlbumForm
that extends from ZendFormForm to define our form.
Create a file called AlbumForm.php in
module/Album/src/Album/Form:
1

namespace AlbumForm;

2
3

use ZendFormForm;

4
5
6
7
8
9
10

class AlbumForm extends Form
{
public function __construct($name = null)
{
// we want to ignore the name passed
parent::__construct(’album’);

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

$this->add(array(
’name’ => ’id’,
’type’ => ’Hidden’,
));
$this->add(array(
’name’ => ’title’,
’type’ => ’Text’,
’options’ => array(
’label’ => ’Title’,
),
));
$this->add(array(
’name’ => ’artist’,
’type’ => ’Text’,
’options’ => array(

29
Zend Framework 2 Documentation, Release 2.2.5

’label’ => ’Artist’,

27

),

28

));
$this->add(array(
’name’ => ’submit’,
’type’ => ’Submit’,
’attributes’ => array(
’value’ => ’Go’,
’id’ => ’submitbutton’,
),
));

29
30
31
32
33
34
35
36
37

}

38
39

}

Within the constructor of AlbumForm we do several things. First, we set the name of the form as we call the parents
constructor. We then set the form’s method, in this case, post. Finally, we create four form elements: the id, title,
artist, and submit button. For each item we set various attributes and options, including the label to be displayed.
We also need to set up validation for this form. In Zend Framework 2 this is done using an input filter, which can either
be standalone or defined within any class that implements the InputFilterAwareInterface interface, such as
a model entity. In our case, we are going to add the input filter to the Album class, which resides in the Album.php
file in module/Album/src/Album/Model:
1

namespace AlbumModel;

2
3
4
5
6

// Add these import statements
use ZendInputFilterInputFilter;
use ZendInputFilterInputFilterAwareInterface;
use ZendInputFilterInputFilterInterface;

7
8
9
10
11
12
13

class Album implements InputFilterAwareInterface
{
public $id;
public $artist;
public $title;
protected $inputFilter;

// <-- Add this variable

14

public function exchangeArray($data)
{
$this->id
= (isset($data[’id’]))
? $data[’id’]
: null;
$this->artist = (isset($data[’artist’])) ? $data[’artist’] : null;
$this->title = (isset($data[’title’])) ? $data[’title’] : null;
}

15
16
17
18
19
20
21

// Add content to these methods:
public function setInputFilter(InputFilterInterface $inputFilter)
{
throw new Exception("Not used");
}

22
23
24
25
26
27

public function getInputFilter()
{
if (!$this->inputFilter) {
$inputFilter = new InputFilter();

28
29
30
31
32

$inputFilter->add(array(
’name’
=> ’id’,
’required’ => true,

33
34
35

30

Chapter 10. Forms and actions
Zend Framework 2 Documentation, Release 2.2.5

’filters’ => array(
array(’name’ => ’Int’),
),

36
37
38

));

39
40

$inputFilter->add(array(
’name’
=> ’artist’,
’required’ => true,
’filters’ => array(
array(’name’ => ’StripTags’),
array(’name’ => ’StringTrim’),
),
’validators’ => array(
array(
’name’
=> ’StringLength’,
’options’ => array(
’encoding’ => ’UTF-8’,
’min’
=> 1,
’max’
=> 100,
),
),
),
));

41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

$inputFilter->add(array(
’name’
=> ’title’,
’required’ => true,
’filters’ => array(
array(’name’ => ’StripTags’),
array(’name’ => ’StringTrim’),
),
’validators’ => array(
array(
’name’
=> ’StringLength’,
’options’ => array(
’encoding’ => ’UTF-8’,
’min’
=> 1,
’max’
=> 100,
),
),
),
));

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

$this->inputFilter = $inputFilter;

79

}

80
81

return $this->inputFilter;

82

}

83
84

}

The InputFilterAwareInterface defines two methods: setInputFilter() and getInputFilter().
We only need to implement getInputFilter() so we simply throw an exception in setInputFilter().
Within getInputFilter(), we instantiate an InputFilter and then add the inputs that we require. We add
one input for each property that we wish to filter or validate. For the id field we add an Int filter as we only need
integers. For the text elements, we add two filters, StripTags and StringTrim, to remove unwanted HTML and
unnecessary white space. We also set them to be required and add a StringLength validator to ensure that the user
doesnt enter more characters than we can store into the database.

10.1. Adding new albums

31
Zend Framework 2 Documentation, Release 2.2.5

We now need to get the form to display and then process it on submission.
AlbumControllers addAction():
1

This is done within the

// module/Album/src/Album/Controller/AlbumController.php:

2
3
4
5
6
7
8

//...
use ZendMvcControllerAbstractActionController;
use ZendViewModelViewModel;
use AlbumModelAlbum;
// <-- Add this import
use AlbumFormAlbumForm;
// <-- Add this import
//...

9

// Add content to this method:
public function addAction()
{
$form = new AlbumForm();
$form->get(’submit’)->setValue(’Add’);

10
11
12
13
14
15

$request = $this->getRequest();
if ($request->isPost()) {
$album = new Album();
$form->setInputFilter($album->getInputFilter());
$form->setData($request->getPost());

16
17
18
19
20
21

if ($form->isValid()) {
$album->exchangeArray($form->getData());
$this->getAlbumTable()->saveAlbum($album);

22
23
24
25

// Redirect to list of albums
return $this->redirect()->toRoute(’album’);

26
27

}
}
return array(’form’ => $form);

28
29
30
31
32

}
//...

After adding the AlbumForm to the use list, we implement addAction(). Lets look at the addAction() code
in a little more detail:
1
2

$form = new AlbumForm();
$form->get(’submit’)->setValue(’Add’);

We instantiate AlbumForm and set the label on the submit button to Add. We do this here as well want to re-use the
form when editing an album and will use a different label.
1
2
3
4
5
6

$request = $this->getRequest();
if ($request->isPost()) {
$album = new Album();
$form->setInputFilter($album->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid()) {

If the Request objects isPost() method is true, then the form has been submitted and so we set the forms
input filter from an album instance. We then set the posted data to the form and check to see if it is valid using the
isValid() member function of the form.
1
2

$album->exchangeArray($form->getData());
$this->getAlbumTable()->saveAlbum($album);

If the form is valid, then we grab the data from the form and store to the model using saveAlbum().
32

Chapter 10. Forms and actions
Zend Framework 2 Documentation, Release 2.2.5

1
2

// Redirect to list of albums
return $this->redirect()->toRoute(’album’);

After we have saved the new album row, we redirect back to the list of albums using the Redirect controller plugin.
1

return array(’form’ => $form);

Finally, we return the variables that we want assigned to the view. In this case, just the form object. Note that Zend
Framework 2 also allows you to simply return an array containing the variables to be assigned to the view and it will
create a ViewModel behind the scenes for you. This saves a little typing.
We now need to render the form in the add.phtml view script:
1
2

<?php
// module/Album/view/album/album/add.phtml:

3
4
5
6
7
8
9
10

$title = ’Add new album’;
$this->headTitle($title);
?>
<h1><?php echo $this->escapeHtml($title); ?></h1>
<?php
$form->setAttribute(’action’, $this->url(’album’, array(’action’ => ’add’)));
$form->prepare();

11
12
13
14
15
16
17

echo
echo
echo
echo
echo
echo

$this->form()->openTag($form);
$this->formHidden($form->get(’id’));
$this->formRow($form->get(’title’));
$this->formRow($form->get(’artist’));
$this->formSubmit($form->get(’submit’));
$this->form()->closeTag();

Again, we display a title as before and then we render the form. Zend Framework provides some view helpers to make
this a little easier. The form() view helper has an openTag() and closeTag() method which we use to open
and close the form. Then for each element with a label, we can use formRow(), but for the two elements that are
standalone, we use formHidden() and formSubmit().

10.1. Adding new albums

33
Zend Framework 2 Documentation, Release 2.2.5

Alternatively, the process of rendering the form can be simplified by using the bundled formCollection view
helper. For example, in the view script above replace all the form-rendering echo statements with:
1

echo $this->formCollection($form);

Note: You still need to call the openTag and closeTag methods of the form. You replace the other echo statements
with the call to formCollection, above.
This will iterate over the form structure, calling the appropriate label, element and error view helpers for each element,
but you still have to wrap formCollection($form) with the open and close form tags. This helps reduce the complexity
of your view script in situations where the default HTML rendering of the form is acceptable.
You should now be able to use the Add new album link on the home page of the application to add a new album record.

10.2 Editing an album
Editing an album is almost identical to adding one, so the code is very similar. This time we use editAction() in
the AlbumController:
1
2

// module/Album/src/Album/Controller/AlbumController.php:
//...

3

// Add content to this method:
public function editAction()
{
$id = (int) $this->params()->fromRoute(’id’, 0);
if (!$id) {
return $this->redirect()->toRoute(’album’, array(
’action’ => ’add’
));
}

4
5
6
7
8
9
10
11
12
13

// Get the Album with the specified id. An exception is thrown
// if it cannot be found, in which case go to the index page.
try {
$album = $this->getAlbumTable()->getAlbum($id);
}
catch (Exception $ex) {
return $this->redirect()->toRoute(’album’, array(
’action’ => ’index’
));
}

14
15
16
17
18
19
20
21
22
23
24

$form = new AlbumForm();
$form->bind($album);
$form->get(’submit’)->setAttribute(’value’, ’Edit’);

25
26
27
28

$request = $this->getRequest();
if ($request->isPost()) {
$form->setInputFilter($album->getInputFilter());
$form->setData($request->getPost());

29
30
31
32
33

if ($form->isValid()) {
$this->getAlbumTable()->saveAlbum($album);

34
35
36

// Redirect to list of albums
return $this->redirect()->toRoute(’album’);

37
38

34

Chapter 10. Forms and actions
Zend Framework 2 Documentation, Release 2.2.5

}

39

}

40
41

return array(
’id’ => $id,
’form’ => $form,
);

42
43
44
45
46
47

}
//...

This code should look comfortably familiar. Lets look at the differences from adding an album. Firstly, we look for
the id that is in the matched route and use it to load the album to be edited:
1
2
3
4
5
6

$id = (int) $this->params()->fromRoute(’id’, 0);
if (!$id) {
return $this->redirect()->toRoute(’album’, array(
’action’ => ’add’
));
}

7
8
9
10
11
12
13
14
15
16
17

// Get the album with the specified id. An exception is thrown
// if it cannot be found, in which case go to the index page.
try {
$album = $this->getAlbumTable()->getAlbum($id);
}
catch (Exception $ex) {
return $this->redirect()->toRoute(’album’, array(
’action’ => ’index’
));
}

params is a controller plugin that provides a convenient way to retrieve parameters from the matched route. We use
it to retrieve the id from the route we created in the modules module.config.php. If the id is zero, then we
redirect to the add action, otherwise, we continue by getting the album entity from the database.
We have to check to make sure that the Album with the specified id can actually be found. If it cannot, then the data
access method throws an exception. We catch that exception and re-route the user to the index page.
1
2
3

$form = new AlbumForm();
$form->bind($album);
$form->get(’submit’)->setAttribute(’value’, ’Edit’);

The forms bind() method attaches the model to the form. This is used in two ways:
• When displaying the form, the initial values for each element are extracted from the model.
• After successful validation in isValid(), the data from the form is put back into the model.
These operations are done using a hydrator object. There are a number of hydrators, but the default one
is ZendStdlibHydratorArraySerializable which expects to find two methods in the model:
getArrayCopy() and exchangeArray(). We have already written exchangeArray() in our Album entity,
so just need to write getArrayCopy():
1
2
3
4
5
6
7

// module/Album/src/Album/Model/Album.php:
// ...
public function exchangeArray($data)
{
$this->id
= (isset($data[’id’]))
? $data[’id’]
: null;
$this->artist = (isset($data[’artist’])) ? $data[’artist’] : null;
$this->title = (isset($data[’title’])) ? $data[’title’] : null;

10.2. Editing an album

35
Zend Framework 2 Documentation, Release 2.2.5

}

8
9
10
11
12
13
14
15

// Add the following method:
public function getArrayCopy()
{
return get_object_vars($this);
}
// ...

As a result of using bind() with its hydrator, we do not need to populate the forms data back into the $album as
thats already been done, so we can just call the mappers saveAlbum() to store the changes back to the database.
The view template, edit.phtml, looks very similar to the one for adding an album:
1
2

<?php
// module/Album/view/album/album/edit.phtml:

3
4
5
6
7

$title = ’Edit album’;
$this->headTitle($title);
?>
<h1><?php echo $this->escapeHtml($title); ?></h1>

8
9
10
11
12
13
14
15
16
17
18

<?php
$form = $this->form;
$form->setAttribute(’action’, $this->url(
’album’,
array(
’action’ => ’edit’,
’id’
=> $this->id,
)
));
$form->prepare();

19
20
21
22
23
24
25

echo
echo
echo
echo
echo
echo

$this->form()->openTag($form);
$this->formHidden($form->get(’id’));
$this->formRow($form->get(’title’));
$this->formRow($form->get(’artist’));
$this->formSubmit($form->get(’submit’));
$this->form()->closeTag();

The only changes are to use the Edit Album title and set the forms action to the edit action too.
You should now be able to edit albums.

10.3 Deleting an album
To round out our application, we need to add deletion. We have a Delete link next to each album on our list page and
the naive approach would be to do a delete when its clicked. This would be wrong. Remembering our HTTP spec, we
recall that you shouldnt do an irreversible action using GET and should use POST instead.
We shall show a confirmation form when the user clicks delete and if they then click yes, we will do the deletion. As
the form is trivial, well code it directly into our view (ZendForm is, after all, optional!).
Lets start with the action code in AlbumController::deleteAction():
1
2
3

// module/Album/src/Album/Controller/AlbumController.php:
//...
// Add content to the following method:

36

Chapter 10. Forms and actions
Zend Framework 2 Documentation, Release 2.2.5

4
5
6
7
8
9

public function deleteAction()
{
$id = (int) $this->params()->fromRoute(’id’, 0);
if (!$id) {
return $this->redirect()->toRoute(’album’);
}

10

$request = $this->getRequest();
if ($request->isPost()) {
$del = $request->getPost(’del’, ’No’);

11
12
13
14

if ($del == ’Yes’) {
$id = (int) $request->getPost(’id’);
$this->getAlbumTable()->deleteAlbum($id);
}

15
16
17
18
19

// Redirect to list of albums
return $this->redirect()->toRoute(’album’);

20
21

}

22
23

return array(
’id’
=> $id,
’album’ => $this->getAlbumTable()->getAlbum($id)
);

24
25
26
27
28
29

}
//...

As before, we get the id from the matched route, and check the request objects isPost() to determine whether
to show the confirmation page or to delete the album. We use the table object to delete the row using the
deleteAlbum() method and then redirect back the list of albums. If the request is not a POST, then we retrieve the
correct database record and assign to the view, along with the id.
The view script is a simple form:
1
2

<?php
// module/Album/view/album/album/delete.phtml:

3
4
5
6
7

$title = ’Delete album’;
$this->headTitle($title);
?>
<h1><?php echo $this->escapeHtml($title); ?></h1>

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

<p>Are you sure that you want to delete
’<?php echo $this->escapeHtml($album->title); ?>’ by
’<?php echo $this->escapeHtml($album->artist); ?>’?
</p>
<?php
$url = $this->url(’album’, array(
’action’ => ’delete’,
’id’
=> $this->id,
));
?>
<form action="<?php echo $url; ?>" method="post">
<div>
<input type="hidden" name="id" value="<?php echo (int) $album->id; ?>" />
<input type="submit" name="del" value="Yes" />
<input type="submit" name="del" value="No" />
</div>
</form>

10.3. Deleting an album

37
Zend Framework 2 Documentation, Release 2.2.5

In this script, we display a confirmation message to the user and then a form with “Yes” and “No” buttons. In the
action, we checked specifically for the Yes value when doing the deletion.

10.4 Ensuring that the home page displays the list of albums
One final point. At the moment, the home page, http://zf2-tutorial.localhost/ doesnt display the list of albums.
This is due to a route set up in the Application modules module.config.php.
module/Application/config/module.config.php and find the home route:
1
2
3
4
5
6
7
8
9
10

To change it, open

’home’ => array(
’type’ => ’ZendMvcRouterHttpLiteral’,
’options’ => array(
’route’
=> ’/’,
’defaults’ => array(
’controller’ => ’ApplicationControllerIndex’,
’action’
=> ’index’,
),
),
),

Change the controller from ApplicationControllerIndex to AlbumControllerAlbum:
1
2
3
4
5
6
7
8
9
10

’home’ => array(
’type’ => ’ZendMvcRouterHttpLiteral’,
’options’ => array(
’route’
=> ’/’,
’defaults’ => array(
’controller’ => ’AlbumControllerAlbum’, // <-- change here
’action’
=> ’index’,
),
),
),

Thats it - you now have a fully working application!

38

Chapter 10. Forms and actions
CHAPTER 11

Conclusion

This concludes our brief look at building a simple, but fully functional, MVC application using Zend Framework 2.
In this tutorial we but briefly touched quite a number of different parts of the framework.
The most important part of applications built with Zend Framework 2 are the modules, the building blocks of any MVC
ZF2 application.
To ease the work with dependencies inside our applications, we use the service manager.
To be able to map a request to controllers and their actions, we use routes.
Data persistence, in most cases, includes using ZendDb to communicate with one of the databases. Input data is
filtered and validated with input filters and together with ZendForm they provide a strong bridge between the domain
model and the view layer.
ZendView is responsible for the View in the MVC stack, together with a vast amount of view helpers.

39
Zend Framework 2 Documentation, Release 2.2.5

40

Chapter 11. Conclusion
CHAPTER 12

Zend Framework Tool (ZFTool)

ZFTool is a utility module for maintaining modular Zend Framework 2 applications. It runs from the command line
and can be installed as ZF2 module or as PHAR (see below). This tool gives you the ability to:
• create a ZF2 project, installing a skeleton application;
• create a new module inside an existing ZF2 application;
• get the list of all the modules installed inside an application;
• get the configuration file of a ZF2 application;
• install the ZF2 library choosing a specific version.
To install the ZFTool you can use one of the following methods or you can just download the PHAR package and use
it.

12.1 Installation using Composer
1. Open console (command prompt)
2. Go to your application’s directory
3. Run composer require zendframework/zftool:dev-master

12.2 Manual installation
1. Clone using git or download zipball
2. Extract to vendor/ZFTool in your ZF2 application
3. Enter the vendor/ZFTool folder and execute zf.php as reported below.

12.3 Without installation, using the PHAR file
1. You don’t need to install ZFTool if you want just use it as a shell command. You can download zftool.phar and
use it.

41
Zend Framework 2 Documentation, Release 2.2.5

12.4 Usage
In the following usage examples, the zf.php command can be replaced with zftool.phar.

12.4.1 Basic information
> zf.php modules [list]

show loaded modules

The modules option gives you the list of all the modules installed in a ZF2 application.
> zf.php version | --version

display current Zend Framework version

The version option gives you the version number of ZFTool and, if executed from the root folder of a ZF2 application,
the version number of the Zend Framework library used by the application.

12.4.2 Project creation
> zf.php create project <path>
<path>

The path of the project to be created

This command installs the ZendSkeletonApplication in the specified path.

12.4.3 Module creation
> zf.php create module <name> [<path>]
<name>
<path>

The name of the module to be created
The path to the root folder of the ZF2 application (optional)

This command can be used to create a new module inside an existing ZF2 application. If the path is not provided the
ZFTool try to create a new module in the local directory (only if the local folder contains a ZF2 application).

12.4.4 Classmap generator
> zf.php classmap generate <directory> <classmap file> [--append|-a] [--overwrite|-w]
<directory>
<classmap file>
--append | -a
--overwrite | -w

The directory to scan for PHP classes (use "." to use current directory)
File name for generated class map file or - for standard output. If not supplied
autoload_classmap.php inside <directory>.
Append to classmap file if it exists
Whether or not to overwrite existing classmap file

12.4.5 ZF library installation
> zf.php install zf <path> [<version>]
<path>
<version>

42

The directory where to install the ZF2 library
The version to install, if not specified uses the last available

Chapter 12. Zend Framework Tool (ZFTool)
Zend Framework 2 Documentation, Release 2.2.5

This command install the specified version of the ZF2 library in a path. If the version is omitted it will be used the
last stable available. Using this command you can install all the tag version specified in the ZF2 github repository
(the name used for the version is obtained removing the ‘release-‘ string from the tag name; for instance, the tag
‘release-2.0.0’ is equivalent to the version number 2.0.0).

12.4.6 Compile the PHAR file
You can create a .phar file containing the ZFTool project. In order to compile ZFTool in a .phar file you need to execute
the following command:
> bin/create-phar

This command will create a zftool.phar file in the bin folder. You can use and ship only this file to execute all the
ZFTool functionalities. After the zftool.phar creation, we suggest to add the folder bin of ZFTool in your PATH
environment. In this way you can execute the zftool.phar script wherever you are.

12.4. Usage

43
Zend Framework 2 Documentation, Release 2.2.5

44

Chapter 12. Zend Framework Tool (ZFTool)
CHAPTER 13

Learning Dependency Injection

13.1 Very brief introduction to Di.
Dependency Injection is a concept that has been talked about in numerous places over the web. For the purposes of
this quickstart, we’ll explain the act of injecting dependencies simply with this below code:
1

$b = new B(new A());

Above, A is a dependency of B, and A was injected into B. If you are not familiar with the concept of dependency
injection, here are a couple of great reads: Matthew Weier O’Phinney’s Analogy, Ralph Schindler’s Learning DI, or
Fabien Potencier’s Series on DI.

13.2 Simplest usage case (2 classes, one consumes the other)
In the simplest use case, a developer might have one class (A) that is consumed by another class (B) through the
constructor. By having the dependency injected through the constructor, this requires an object of type A be instantiated
before an object of type B so that A can be injected into B.
1

namespace My {

2

class A
{
/* Some useful functionality */
}

3
4
5
6
7

class B
{
protected $a = null;
public function __construct(A $a)
{
$this->a = $a;
}
}

8
9
10
11
12
13
14
15
16

}

To create B by hand, a developer would follow this work flow, or a similar workflow to this:

45
Zend Framework 2 Documentation, Release 2.2.5

1

$b = new B(new A());

If this workflow becomes repeated throughout your application multiple times, this creates an opportunity where one
might want to DRY up the code. While there are several ways to do this, using a dependency injection container is one
of these solutions. With Zend’s dependency injection container ZendDiDi, the above use case can be taken care
of with no configuration (provided all of your autoloading is already configured properly) with the following usage:
1
2

$di = new ZendDiDi;
$b = $di->get(’MyB’); // will produce a B object that is consuming an A object

Moreover, by using the Di::get() method, you are ensuring that the same exact object is returned on subsequent
calls. To force new objects to be created on each and every request, one would use the Di::newInstance()
method:
1

$b = $di->newInstance(’MyB’);

Let’s assume for a moment that A requires some configuration before it can be created. Our previous use case is
expanded to this (we’ll throw a 3rd class in for good measure):
1

namespace My {

2

class A
{
protected $username = null;
protected $password = null;
public function __construct($username, $password)
{
$this->username = $username;
$this->password = $password;
}
}

3
4
5
6
7
8
9
10
11
12
13

class B
{
protected $a = null;
public function __construct(A $a)
{
$this->a = $a;
}
}

14
15
16
17
18
19
20
21
22

class C
{
protected $b = null;
public function __construct(B $b)
{
$this->b = $b;
}
}

23
24
25
26
27
28
29
30
31
32

}

With the above, we need to ensure that our Di is capable of setting the A class with a few configuration values (which
are generally scalar in nature). To do this, we need to interact with the InstanceManager:
1
2
3

$di = new ZendDiDi;
$di->getInstanceManager()->setProperty(’A’, ’username’, ’MyUsernameValue’);
$di->getInstanceManager()->setProperty(’A’, ’password’, ’MyHardToGuessPassword%$#’);

46

Chapter 13. Learning Dependency Injection
Zend Framework 2 Documentation, Release 2.2.5

Now that our container has values it can use when creating A, and our new goal is to have a C object that consumes B
and in turn consumes A, the usage scenario is still the same:
1
2
3

$c = $di->get(’MyC’);
// or
$c = $di->newInstance(’MyC’);

Simple enough, but what if we wanted to pass in these parameters at call time? Assuming a default Di object ($di =
new ZendDiDi() without any configuration to the InstanceManager), we could do the following:
1
2
3
4

$parameters = array(
’username’ => ’MyUsernameValue’,
’password’ => ’MyHardToGuessPassword%$#’,
);

5
6
7
8

$c = $di->get(’MyC’, $parameters);
// or
$c = $di->newInstance(’MyC’, $parameters);

Constructor injection is not the only supported type of injection. The other most popular method of injection is also
supported: setter injection. Setter injection allows one to have a usage scenario that is the same as our previous
example with the exception, for example, of our B class now looking like this:
1
2
3
4
5
6
7
8
9
10

namespace My {
class B
{
protected $a;
public function setA(A $a)
{
$this->a = $a;
}
}
}

Since the method is prefixed with set, and is followed by a capital letter, the Di knows that this method is used for
setter injection, and again, the use case $c = $di->get(’C’), will once again know how to fill the dependencies
when needed to create an object of type C.
Other methods are being created to determine what the wirings between classes are, such as interface injection and
annotation based injection.

13.3 Simplest Usage Case Without Type-hints
If your code does not have type-hints or you are using 3rd party code that does not have type-hints but does practice
dependency injection, you can still use the Di, but you might find you need to describe your dependencies explicitly.
To do this, you will need to interact with one of the definitions that is capable of letting a developer describe, with
objects, the map between classes. This particular definition is called the BuilderDefinition and can work with,
or in place of, the default RuntimeDefinition.
Definitions are a part of the Di that attempt to describe the relationship between classes so that
Di::newInstance() and Di::get() can know what the dependencies are that need to be filled for a particular
class/object. With no configuration, Di will use the RuntimeDefinition which uses reflection and the type-hints
in your code to determine the dependency map. Without type-hints, it will assume that all dependencies are scalar or
required configuration parameters.
The BuilderDefinition, which can be used in tandem with the RuntimeDefinition (technically, it can
be used in tandem with any definition by way of the AggregateDefinition), allows you to programmatically

13.3. Simplest Usage Case Without Type-hints

47
Zend Framework 2 Documentation, Release 2.2.5

describe the mappings with objects. Let’s say for example, our above A/B/C usage scenario, were altered such that
class B now looks like this:
1
2
3
4
5
6
7
8
9
10

namespace My {
class B
{
protected $a;
public function setA($a)
{
$this->a = $a;
}
}
}

You’ll notice the only change is that setA now does not include any type-hinting information.
1
2
3

use ZendDiDi;
use ZendDiDefinition;
use ZendDiDefinitionBuilder;

4
5
6
7

// Describe this class:
$builder = new DefinitionBuilderDefinition;
$builder->addClass(($class = new BuilderPhpClass));

8
9
10

$class->setName(’MyB’);
$class->addInjectableMethod(($im = new BuilderInjectableMethod));

11
12
13

$im->setName(’setA’);
$im->addParameter(’a’, ’MyA’);

14
15
16
17
18
19

// Use both our Builder Definition as well as the default
// RuntimeDefinition, builder first
$aDef = new DefinitionAggregateDefinition;
$aDef->addDefinition($builder);
$aDef->addDefinition(new DefinitionRuntimeDefinition);

20
21
22
23

// Now make sure the Di understands it
$di = new Di;
$di->setDefinition($aDef);

24
25
26
27
28
29

// and finally, create C
$parameters = array(
’username’ => ’MyUsernameValue’,
’password’ => ’MyHardToGuessPassword%$#’,
);

30
31

$c = $di->get(’MyC’, $parameters);

This above usage scenario provides that whatever the code looks like, you can ensure that it works with the dependency
injection container. In an ideal world, all of your code would have the proper type hinting and/or would be using a
mapping strategy that reduces the amount of bootstrapping work that needs to be done in order to have a full definition
that is capable of instantiating all of the objects you might require.

13.4 Simplest usage case with Compiled Definition
Without going into the gritty details, as you might expect, PHP at its core is not DI friendly. Out-of-the-box, the Di
uses a RuntimeDefinition which does all class map resolution via PHP’s Reflection extension. Couple that
48

Chapter 13. Learning Dependency Injection
Zend Framework 2 Documentation, Release 2.2.5

with the fact that PHP does not have a true application layer capable of storing objects in-memory between requests,
and you get a recipe that is less performant than similar solutions you’ll find in Java and .Net (where there is an
application layer with in-memory object storage.)
To mitigate this shortcoming, ZendDi has several features built in capable of pre-compiling the most expensive
tasks that surround dependency injection. It is worth noting that the RuntimeDefinition, which is used by
default, is the only definition that does lookups on-demand. The rest of the Definition objects are capable of
being aggregated and stored to disk in a very performant way.
Ideally, 3rd party code will ship with a pre-compiled Definition that will describe the various relationships and
parameter/property needs of each class that is to be instantiated. This Definition would have been built as part of
some deployment or packaging task by this 3rd party. When this is not the case, you can create these Definitions
via any of the Definition types provided with the exception of the RuntimeDefinition. Here is a breakdown
of the job of each definition type:
• AggregateDefinition- Aggregates multiple definitions of various types. When looking for a class, it
looks it up in the order the definitions were provided to this aggregate.
• ArrayDefinition- This definition takes an array of information and exposes it via the interface provided
by ZendDiDefinition suitable for usage by Di or an AggregateDefinition
• BuilderDefinition- Creates a definition based on an object graph consisting of various
BuilderPhpClass objects and BuilderInjectionMethod objects that describe the mapping needs
of the target codebase and
• Compiler- This is not actually a definition, but produces an ArrayDefinition based off of a code scanner
(ZendCodeScannerDirectoryScanner or ZendCodeScannerFileScanner).
The following is an example of producing a definition via a DirectoryScanner:
1
2
3
4
5

$compiler = new ZendDiDefinitionCompiler();
$compiler->addCodeScannerDirectory(
new ZendCodeScannerScannerDirectory(’path/to/library/My/’)
);
$definition = $compiler->compile();

This definition can then be directly used by the Di (assuming the above A, B, C scenario was actually a file per
class on disk):
1
2
3
4
5

$di = new ZendDiDi;
$di->setDefinition($definition);
$di->getInstanceManager()->setProperty(’MyA’, ’username’, ’foo’);
$di->getInstanceManager()->setProperty(’MyA’, ’password’, ’bar’);
$c = $di->get(’MyC’);

One strategy for persisting these compiled definitions would be the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14

if (!file_exists(__DIR__ . ’/di-definition.php’) && $isProduction) {
$compiler = new ZendDiDefinitionCompiler();
$compiler->addCodeScannerDirectory(
new ZendCodeScannerScannerDirectory(’path/to/library/My/’)
);
$definition = $compiler->compile();
file_put_contents(
__DIR__ . ’/di-definition.php’,
’<?php return ’ . var_export($definition->toArray(), true) . ’;’
);
} else {
$definition = new ZendDiDefinitionArrayDefinition(
include __DIR__ . ’/di-definition.php’
);

13.4. Simplest usage case with Compiled Definition

49
Zend Framework 2 Documentation, Release 2.2.5

15

}

16
17
18

// $definition can now be used; in a production system it will be written
// to disk.

Since ZendCodeScanner does not include files, the classes contained within are not loaded into memory. Instead, ZendCodeScanner uses tokenization to determine the structure of your files. This makes this suitable to
use this solution during development and within the same request as any one of your application’s dispatched actions.

13.5 Creating a precompiled definition for others to use
If you are a 3rd party code developer, it makes sense to produce a Definition file that describes your code so that
others can utilize this Definition without having to Reflect it via the RuntimeDefinition, or create it via
the Compiler. To do this, use the same technique as above. Instead of writing the resulting array to disk, you would
write the information into a definition directly, by way of ZendCodeGenerator:
1
2
3
4
5
6
7

// First, compile the information
$compiler = new ZendDiDefinitionCompilerDefinition();
$compiler->addDirectoryScanner(
new ZendCodeScannerDirectoryScanner(__DIR__ . ’/My/’)
);
$compiler->compile();
$definition = $compiler->toArrayDefinition();

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

// Now, create a Definition class for this information
$codeGenerator = new ZendCodeGeneratorFileGenerator();
$codeGenerator->setClass(($class = new ZendCodeGeneratorClassGenerator()));
$class->setNamespaceName(’My’);
$class->setName(’DiDefinition’);
$class->setExtendedClass(’ZendDiDefinitionArrayDefinition’);
$class->addMethod(
’__construct’,
array(),
ZendCodeGeneratorMethodGenerator::FLAG_PUBLIC,
’parent::__construct(’ . var_export($definition->toArray(), true) . ’);’
);
file_put_contents(__DIR__ . ’/My/DiDefinition.php’, $codeGenerator->generate());

13.6 Using Multiple Definitions From Multiple Sources
In all actuality, you will be using code from multiple places, some Zend Framework code, some other 3rd party code,
and of course, your own code that makes up your application. Here is a method for consuming definitions from
multiple places:
1
2
3

use ZendDiDi;
use ZendDiDefinition;
use ZendDiDefinitionBuilder;

4
5
6

$di = new Di;
$diDefAggregate = new DefinitionAggregate();

7
8
9

// first add in provided Definitions, for example
$diDefAggregate->addDefinition(new ThirdPartyDbalDiDefinition());

50

Chapter 13. Learning Dependency Injection
Zend Framework 2 Documentation, Release 2.2.5

10

$diDefAggregate->addDefinition(new ZendControllerDiDefinition());

11
12
13
14
15
16
17
18
19
20
21

// for code that does not have TypeHints
$builder = new DefinitionBuilderDefinition();
$builder->addClass(($class = BuilderPhpClass));
$class->addInjectionMethod(
($injectMethod = new BuilderInjectionMethod())
);
$injectMethod->setName(’injectImplementation’);
$injectMethod->addParameter(
’implementation’, ’ClassForSpecificImplementation’
);

22
23
24
25
26
27
28
29

// now, your application code
$compiler = new DefinitionCompiler()
$compiler->addCodeScannerDirectory(
new ZendCodeScannerDirectoryScanner(__DIR__ . ’/App/’)
);
$appDefinition = $compiler->compile();
$diDefAggregate->addDefinition($appDefinition);

30
31
32

// now, pass in properties
$im = $di->getInstanceManager();

33
34
35
36
37
38
39
40
41
42
43
44

// this could come from ZendConfigConfig::toArray
$propertiesFromConfig = array(
’ThirdPartyDbalDbAdapter’ => array(
’username’ => ’someUsername’,
’password’ => ’somePassword’
),
’ZendControllerHelperContentType’ => array(
’default’ => ’xhtml5’
),
);
$im->setProperties($propertiesFromConfig);

13.7 Generating Service Locators
In production, you want things to be as fast as possible. The Dependency Injection Container, while engineered for
speed, still must do a fair bit of work resolving parameters and dependencies at runtime. What if you could speed
things up and remove those lookups?
The ZendDiServiceLocatorGenerator component can do just that. It takes a configured DI instance,
and generates a service locator class for you from it. That class will manage instances for you, as well as provide
hard-coded, lazy-loading instantiation of instances.
The method getCodeGenerator() returns an instance of ZendCodeGeneratorPhpPhpFile, from
which you can then write a class file with the new Service Locator. Methods on the Generator class allow you
to specify the namespace and class for the generated Service Locator.
As an example, consider the following:
1

use ZendDiServiceLocatorGenerator;

2
3
4

// $di is a fully configured DI instance
$generator = new Generator($di);

13.7. Generating Service Locators

51
Zend Framework 2 Documentation, Release 2.2.5

5
6
7
8
9
10

$generator->setNamespace(’Application’)
->setContainerClass(’Context’);
$file = $generator->getCodeGenerator();
$file->setFilename(__DIR__ . ’/../Application/Context.php’);
$file->write();

The above code will write to ../Application/Context.php, and that file will contain the class
ApplicationContext. That file might look like the following:
1

<?php

2
3

namespace Application;

4
5

use ZendDiServiceLocator;

6
7
8

class Context extends ServiceLocator
{

9

public function get($name, array $params = array())
{
switch ($name) {
case ’composed’:
case ’MyComposedClass’:
return $this->getMyComposedClass();

10
11
12
13
14
15
16

case ’struct’:
case ’MyStruct’:
return $this->getMyStruct();

17
18
19
20

default:
return parent::get($name, $params);

21
22

}

23

}

24
25

public function getComposedClass()
{
if (isset($this->services[’MyComposedClass’])) {
return $this->services[’MyComposedClass’];
}

26
27
28
29
30
31

$object = new MyComposedClass();
$this->services[’MyComposedClass’] = $object;
return $object;

32
33
34

}
public function getMyStruct()
{
if (isset($this->services[’MyStruct’])) {
return $this->services[’MyStruct’];
}

35
36
37
38
39
40
41

$object = new MyStruct();
$this->services[’MyStruct’] = $object;
return $object;

42
43
44

}

45
46

public function getComposed()
{
return $this->get(’MyComposedClass’);

47
48
49

52

Chapter 13. Learning Dependency Injection
Zend Framework 2 Documentation, Release 2.2.5

}

50
51

public function getStruct()
{
return $this->get(’MyStruct’);
}

52
53
54
55
56

}

To use this class, you simply consume it as you would a DI container:
1

$container = new ApplicationContext;

2
3

$struct = $container->get(’struct’); // MyStruct instance

One note about this functionality in its current incarnation. Configuration is per-environment only at this time. This
means that you will need to generate a container per execution environment. Our recommendation is that you do so,
and then in your environment, specify the container class to use.

13.7. Generating Service Locators

53
Zend Framework 2 Documentation, Release 2.2.5

54

Chapter 13. Learning Dependency Injection
CHAPTER 14

Unit Testing a Zend Framework 2
application

A solid unit test suite is essential for ongoing development in large projects, especially those with many people involved. Going back and manually testing every individual component of an application after every change is impractical. Your unit tests will help alleviate that by automatically testing your application’s components and alerting you
when something is not working the same way it was when you wrote your tests.
This tutorial is written in the hopes of showing how to test different parts of a Zend Framework 2 MVC application.
As such, this tutorial will use the application written in the getting started user guide. It is in no way a guide to unit
testing in general, but is here only to help overcome the initial hurdles in writing unit tests for ZF2 applications.
It is recommended to have at least a basic understanding of unit tests, assertions and mocks.
As the Zend Framework 2 API uses PHPUnit, so will this tutorial. This tutorial assumes that you already have PHPUnit
installed. The version of PHPUnit used should be 3.7.*

14.1 Setting up the tests directory
As Zend Framework 2 applications are built from modules that should be standalone blocks of an application, we
don’t test the application in it’s entirety, but module by module.
We will show how to set up the minimum requirements to test a module, the Album module we wrote in the user
guide, and which then can be used as a base for testing any other module.
Start by creating a directory called test in zf2-tutorialmoduleAlbum with the following subdirectories:
zf2-tutorial/
/module
/Album
/test
/AlbumTest
/Controller

The structure of the test directory matches exactly with that of the module’s source files, and it will allow you to
keep your tests well-organized and easy to find.

55
Zend Framework 2 Documentation, Release 2.2.5

14.2 Bootstrapping your tests
Next, create a file called phpunit.xml under zf2-tutorial/module/Album/test:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="Bootstrap.php" colors="true">
<testsuites>
<testsuite name="zf2tutorial">
<directory>./AlbumTest</directory>
</testsuite>
</testsuites>
</phpunit>

And a file called Bootstrap.php, also under zf2-tutorial/module/Album/test:
1

<?php

2
3

namespace AlbumTest;

4
5
6
7
8

use
use
use
use

ZendLoaderAutoloaderFactory;
ZendMvcServiceServiceManagerConfig;
ZendServiceManagerServiceManager;
RuntimeException;

9
10
11

error_reporting(E_ALL | E_STRICT);
chdir(__DIR__);

12
13
14
15
16
17
18

/**
* Test bootstrap, for setting up autoloading
*/
class Bootstrap
{
protected static $serviceManager;

19

public static function init()
{
$zf2ModulePaths = array(dirname(dirname(__DIR__)));
if (($path = static::findParentPath(’vendor’))) {
$zf2ModulePaths[] = $path;
}
if (($path = static::findParentPath(’module’)) !== $zf2ModulePaths[0]) {
$zf2ModulePaths[] = $path;
}

20
21
22
23
24
25
26
27
28
29

static::initAutoloader();

30
31

// use ModuleManager to load this module and it’s dependencies
$config = array(
’module_listener_options’ => array(
’module_paths’ => $zf2ModulePaths,
),
’modules’ => array(
’Album’
)
);

32
33
34
35
36
37
38
39
40
41

$serviceManager = new ServiceManager(new ServiceManagerConfig());

42

56

Chapter 14. Unit Testing a Zend Framework 2 application
Zend Framework 2 Documentation, Release 2.2.5

$serviceManager->setService(’ApplicationConfig’, $config);
$serviceManager->get(’ModuleManager’)->loadModules();
static::$serviceManager = $serviceManager;

43
44
45
46

}

47
48
49
50
51
52

public static function chroot()
{
$rootPath = dirname(static::findParentPath(’module’));
chdir($rootPath);
}

53
54
55
56
57

public static function getServiceManager()
{
return static::$serviceManager;
}

58
59
60
61

protected static function initAutoloader()
{
$vendorPath = static::findParentPath(’vendor’);

62

$zf2Path = getenv(’ZF2_PATH’);
if (!$zf2Path) {
if (defined(’ZF2_PATH’)) {
$zf2Path = ZF2_PATH;
} elseif (is_dir($vendorPath
$zf2Path = $vendorPath .
} elseif (is_dir($vendorPath
$zf2Path = $vendorPath .
}
}

63
64
65
66
67
68
69
70
71
72

. ’/ZF2/library’)) {
’/ZF2/library’;
. ’/zendframework/zendframework/library’)) {
’/zendframework/zendframework/library’;

73

if (!$zf2Path) {
throw new RuntimeException(
’Unable to load ZF2. Run ‘php composer.phar install‘ or’
. ’ define a ZF2_PATH environment variable.’
);
}

74
75
76
77
78
79
80

if (file_exists($vendorPath . ’/autoload.php’)) {
include $vendorPath . ’/autoload.php’;
}

81
82
83
84

include $zf2Path . ’/Zend/Loader/AutoloaderFactory.php’;
AutoloaderFactory::factory(array(
’ZendLoaderStandardAutoloader’ => array(
’autoregister_zf’ => true,
’namespaces’ => array(
__NAMESPACE__ => __DIR__ . ’/’ . __NAMESPACE__,
),
),
));

85
86
87
88
89
90
91
92
93
94

}

95
96
97
98
99
100

protected static function findParentPath($path)
{
$dir = __DIR__;
$previousDir = ’.’;
while (!is_dir($dir . ’/’ . $path)) {

14.2. Bootstrapping your tests

57
Zend Framework 2 Documentation, Release 2.2.5

$dir = dirname($dir);
if ($previousDir === $dir) {
return false;
}
$previousDir = $dir;

101
102
103
104
105

}
return $dir . ’/’ . $path;

106
107

}

108
109

}

110
111
112

Bootstrap::init();
Bootstrap::chroot();

The contents of this bootstrap file can be daunting at first sight, but all it really does is ensuring that all the necessary
files are autoloadable for our tests. The most important lines is line 38 on which we say what modules we want to load
for our test. In this case we are only loading the Album module as it has no dependencies against other modules.
Now, if you navigate to the zf2-tutorial/module/Album/test/ directory, and run phpunit, you should
get a similar output to this:
PHPUnit 3.7.13 by Sebastian Bergmann.
Configuration read from /var/www/zf2-tutorial/module/Album/test/phpunit.xml
Time: 0 seconds, Memory: 1.75Mb
No tests executed!

Even though no tests were executed, we at least know that the autoloader found the ZF2 files, otherwise it would throw
a RuntimeException, defined on line 69 of our bootstrap file.

14.3 Your first controller test
Testing controllers is never an easy task, but Zend Framework 2 comes with the ZendTest component which should
make testing much less cumbersome.
First, create IndexControllerTest.php under zf2-tutorial/module/Album/test/AlbumTest/Controller
with the following contents:
<?php
namespace AlbumTestController;
use ZendTestPHPUnitControllerAbstractHttpControllerTestCase;
class AlbumControllerTest extends AbstractHttpControllerTestCase
{
public function setUp()
{
$this->setApplicationConfig(
include ’/var/www/zf2-tutorial/config/application.config.php’
);
parent::setUp();
}
}

58

Chapter 14. Unit Testing a Zend Framework 2 application
Zend Framework 2 Documentation, Release 2.2.5

The AbstractHttpControllerTestCase class we extend here helps us setting up the application itself, helps
with dispatching and other tasks that happen during a request, as well offers methods for asserting request params,
response headers, redirects and more. See ZendTest documentation for more.
One thing that is needed is to set the application config with the setApplicationConfig method.
Now, add the following function to the AlbumControllerTest class:
public function testIndexActionCanBeAccessed()
{
$this->dispatch(’/album’);
$this->assertResponseStatusCode(200);
$this->assertModuleName(’Album’);
$this->assertControllerName(’AlbumControllerAlbum’);
$this->assertControllerClass(’AlbumController’);
$this->assertMatchedRouteName(’album’);
}

This test case dispatches the /album URL, asserts that the response code is 200, and that we ended up in the desired
module and controller.
Note: For asserting the controller name we are using the controller name we defined in our routing configuration for
the Album module. In our example this should be defined on line 19 of the module.config.php file in the Album
module.

14.4 A failing test case
Finally, cd to zf2-tutorial/module/Album/test/ and run phpunit. Uh-oh! The test failed!
PHPUnit 3.7.13 by Sebastian Bergmann.
Configuration read from /var/www/zf2-tutorial/module/Album/test/phpunit.xml
F
Time: 0 seconds, Memory: 8.50Mb
There was 1 failure:
1) AlbumTestControllerAlbumControllerTest::testIndexActionCanBeAccessed
Failed asserting response code "200", actual status code is "500"

/var/www/zf2-tutorial/vendor/ZF2/library/Zend/Test/PHPUnit/Controller/AbstractControllerTestCase.php:
/var/www/zf2-tutorial/module/Album/test/AlbumTest/Controller/AlbumControllerTest.php:22
FAILURES!
Tests: 1, Assertions: 0, Failures: 1.

The failure message doesn’t tell us much, apart from that the expected status code is not 200, but 500. To get a bit
more information when something goes wrong in a test case, we set the protected $traceError member to true.
Add the following just above the setUp method in our AlbumControllerTest class:
protected $traceError = true;

14.4. A failing test case

59
Zend Framework 2 Documentation, Release 2.2.5

Running the phpunit command again and we should see some more information about what went wrong in our test.
The main error message we are interested in should read something like:
ZendServiceManagerExceptionServiceNotFoundException: ZendServiceManagerServiceManager::get
was unable to fetch or create an instance for ZendDbAdapterAdapter

From this error message it is clear that not all our dependencies are available in the service manager. Let us take a look
how can we fix this.

14.5 Configuring the service manager for the tests
The error says that the service manager can not create an instance of a database adapter for us. The database adapter
is indirectly used by our AlbumModelAlbumTable to fetch the list of albums from the database.
The first thought would be to create an instance of an adapter, pass it to the service manager and let the code run from
there as is. The problem with this approach is that we would end up with our test cases actually doing queries against
the database. To keep our tests fast, and to reduce the number of possible failure points in our tests, this should be
avoided.
The second thought would be then to create a mock of the database adapter, and prevent the actual database calls by
mocking them out. This is a much better approach, but creating the adapter mock is tedious (but no doubt we will have
to create it at one point).
The best thing to do would be to mock out our AlbumModelAlbumTable class which retrieves the list of albums
from the database. Remember, we are now testing our controller, so we can mock out the actual call to fetchAll
and replace the return values with dummy values. At this point, we are not interested in how fetchAll retrieves the
albums, but only that it gets called and that it returns an array of albums, so that is why we can get away with this
mocking. When we will test AlbumTable itself, then we will write the actual tests for the fetchAll method.
Here is how we can accomplish this, by modifying the testIndexActionCanBeAccessed test method as follows:
1
2
3
4
5

public function testIndexActionCanBeAccessed()
{
$albumTableMock = $this->getMockBuilder(’AlbumModelAlbumTable’)
->disableOriginalConstructor()
->getMock();

6

$albumTableMock->expects($this->once())
->method(’fetchAll’)
->will($this->returnValue(array()));

7
8
9
10

$serviceManager = $this->getApplicationServiceLocator();
$serviceManager->setAllowOverride(true);
$serviceManager->setService(’AlbumModelAlbumTable’, $albumTableMock);

11
12
13
14

$this->dispatch(’/album’);
$this->assertResponseStatusCode(200);

15
16
17

$this->assertModuleName(’Album’);
$this->assertControllerName(’AlbumControllerAlbum’);
$this->assertControllerClass(’AlbumController’);
$this->assertMatchedRouteName(’album’);

18
19
20
21
22

}

By default, the Service Manager does not allow us to replace existing services. As the AlbumModelAlbumTable
was already set, we are allowing for overrides (line 12), and then replacing the real instance of the AlbumTable with

60

Chapter 14. Unit Testing a Zend Framework 2 application
Zend Framework 2 Documentation, Release 2.2.5

a mock. The mock is created so that it will return just an empty array when the fetchAll method is called. This
allows us to test for what we care about in this test, and that is that by dispatching to the /album URL we get to the
Album module’s AlbumController.
Running the phpunit command at this point, we will get the following output as the tests now pass:
PHPUnit 3.7.13 by Sebastian Bergmann.
Configuration read from /var/www/zf2-tutorial/module/Album/test/phpunit.xml
.
Time: 0 seconds, Memory: 9.00Mb
OK (1 test, 6 assertions)

14.6 Testing actions with POST
One of the most common actions happening in controllers is submitting a form with some POST data. Testing this is
surprisingly easy:
public function testAddActionRedirectsAfterValidPost()
{
$albumTableMock = $this->getMockBuilder(’AlbumModelAlbumTable’)
->disableOriginalConstructor()
->getMock();
$albumTableMock->expects($this->once())
->method(’saveAlbum’)
->will($this->returnValue(null));
$serviceManager = $this->getApplicationServiceLocator();
$serviceManager->setAllowOverride(true);
$serviceManager->setService(’AlbumModelAlbumTable’, $albumTableMock);
$postData = array(
’title’ => ’Led Zeppelin III’,
’artist’ => ’Led Zeppelin’,
);
$this->dispatch(’/album/add’, ’POST’, $postData);
$this->assertResponseStatusCode(302);
$this->assertRedirectTo(’/album’);
}

Here we test that when we make a POST request against the /album/add URL, the
AlbumModelAlbumTable‘s saveAlbum will be called and after that we will be redirected back to the
/album URL.
Running phpunit gives us the following output:
PHPUnit 3.7.13 by Sebastian Bergmann.
Configuration read from /home/robert/www/zf2-tutorial/module/Album/test/phpunit.xml
..

14.6. Testing actions with POST

61
Zend Framework 2 Documentation, Release 2.2.5

Time: 0 seconds, Memory: 10.75Mb
OK (2 tests, 9 assertions)

Testing the editAction and deleteAction methods can be easily done in a manner similar as shown for the
addAction.

14.7 Testing model entities
Now that we know how to test our controllers, let us move to an other important part of our application - the model
entity.
Here we want to test that the initial state of the entity is what we expect it to be, that we can convert the model’s
parameters to and from an array, and that it has all the input filters we need.
Create the file AlbumTest.php in module/Album/test/AlbumTest/Model directory with the following
contents:
1
2

<?php
namespace AlbumTestModel;

3
4
5

use AlbumModelAlbum;
use PHPUnit_Framework_TestCase;

6
7
8
9
10
11

class AlbumTest extends PHPUnit_Framework_TestCase
{
public function testAlbumInitialState()
{
$album = new Album();

12

$this->assertNull(
$album->artist,
’"artist" should initially be null’
);
$this->assertNull(
$album->id,
’"id" should initially be null’
);
$this->assertNull(
$album->title,
’"title" should initially be null’
);

13
14
15
16
17
18
19
20
21
22
23
24

}

25
26

public function testExchangeArraySetsPropertiesCorrectly()
{
$album = new Album();
$data = array(’artist’ => ’some artist’,
’id’
=> 123,
’title’ => ’some title’);

27
28
29
30
31
32
33

$album->exchangeArray($data);

34
35

$this->assertSame(
$data[’artist’],
$album->artist,
’"artist" was not set correctly’

36
37
38
39

62

Chapter 14. Unit Testing a Zend Framework 2 application
Zend Framework 2 Documentation, Release 2.2.5

);
$this->assertSame(
$data[’id’],
$album->id,
’"id" was not set correctly’
);
$this->assertSame(
$data[’title’],
$album->title,
’"title" was not set correctly’
);

40
41
42
43
44
45
46
47
48
49
50
51

}

52
53
54
55

public function testExchangeArraySetsPropertiesToNullIfKeysAreNotPresent()
{
$album = new Album();

56

$album->exchangeArray(array(’artist’ => ’some artist’,
’id’
=> 123,
’title’ => ’some title’));
$album->exchangeArray(array());

57
58
59
60
61

$this->assertNull(
$album->artist, ’"artist" should have defaulted to null’
);
$this->assertNull(
$album->id, ’"id" should have defaulted to null’
);
$this->assertNull(
$album->title, ’"title" should have defaulted to null’
);

62
63
64
65
66
67
68
69
70
71

}

72
73
74
75
76
77
78

public function testGetArrayCopyReturnsAnArrayWithPropertyValues()
{
$album = new Album();
$data = array(’artist’ => ’some artist’,
’id’
=> 123,
’title’ => ’some title’);

79
80
81

$album->exchangeArray($data);
$copyArray = $album->getArrayCopy();

82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

$this->assertSame(
$data[’artist’],
$copyArray[’artist’],
’"artist" was not set correctly’
);
$this->assertSame(
$data[’id’],
$copyArray[’id’],
’"id" was not set correctly’
);
$this->assertSame(
$data[’title’],
$copyArray[’title’],
’"title" was not set correctly’
);

14.7. Testing model entities

63
Zend Framework 2 Documentation, Release 2.2.5

}

98
99

public function testInputFiltersAreSetCorrectly()
{
$album = new Album();

100
101
102
103

$inputFilter = $album->getInputFilter();

104
105

$this->assertSame(3, $inputFilter->count());
$this->assertTrue($inputFilter->has(’artist’));
$this->assertTrue($inputFilter->has(’id’));
$this->assertTrue($inputFilter->has(’title’));

106
107
108
109

}

110
111

}

We are testing for 5 things:
1. Are all of the Album’s properties initially set to NULL?
2. Will the Album’s properties be set correctly when we call exchangeArray()?
3. Will a default value of NULL be used for properties whose keys are not present in the $data array?
4. Can we get an array copy of our model?
5. Do all elements have input filters present?
If we run phpunit again, we will get the following output, confirming that our model is indeed correct:
PHPUnit 3.7.13 by Sebastian Bergmann.
Configuration read from /var/www/zf2-tutorial/module/Album/test/phpunit.xml
.......
Time: 0 seconds, Memory: 11.00Mb
OK (7 tests, 25 assertions)

14.8 Testing model tables
The final step in this unit testing tutorial for Zend Framework 2 applications is writing tests for our model tables.
This test assures that we can get a list of albums, or one album by it’s ID, and that we can save and delete albums from
the database.
To avoid actual interaction with the database itself, we will replace certain parts with mocks.
Create a file AlbumTableTest.php in module/Album/test/AlbumTest/Model with the following contents:
<?php
namespace AlbumTestModel;
use
use
use
use

64

AlbumModelAlbumTable;
AlbumModelAlbum;
ZendDbResultSetResultSet;
PHPUnit_Framework_TestCase;

Chapter 14. Unit Testing a Zend Framework 2 application
Zend Framework 2 Documentation, Release 2.2.5

class AlbumTableTest extends PHPUnit_Framework_TestCase
{
public function testFetchAllReturnsAllAlbums()
{
$resultSet = new ResultSet();
$mockTableGateway = $this->getMock(
’ZendDbTableGatewayTableGateway’,
array(’select’),
array(),
’’,
false
);
$mockTableGateway->expects($this->once())
->method(’select’)
->with()
->will($this->returnValue($resultSet));
$albumTable = new AlbumTable($mockTableGateway);
$this->assertSame($resultSet, $albumTable->fetchAll());
}
}

Since we are testing the AlbumTable here and not the TableGateway class (which has already been tested in
Zend Framework), we just want to make sure that our AlbumTable class is interacting with the TableGateway
class the way that we expect it to. Above, we’re testing to see if the fetchAll() method of AlbumTable will
call the select() method of the $tableGateway property with no parameters. If it does, it should return a
ResultSet object. Finally, we expect that this same ResultSet object will be returned to the calling method.
This test should run fine, so now we can add the rest of the test methods:
public function testCanRetrieveAnAlbumByItsId()
{
$album = new Album();
$album->exchangeArray(array(’id’
=> 123,
’artist’ => ’The Military Wives’,
’title’ => ’In My Dreams’));
$resultSet = new ResultSet();
$resultSet->setArrayObjectPrototype(new Album());
$resultSet->initialize(array($album));
$mockTableGateway = $this->getMock(
’ZendDbTableGatewayTableGateway’,
array(’select’),
array(),
’’,
false
);
$mockTableGateway->expects($this->once())
->method(’select’)
->with(array(’id’ => 123))
->will($this->returnValue($resultSet));
$albumTable = new AlbumTable($mockTableGateway);
$this->assertSame($album, $albumTable->getAlbum(123));
}

14.8. Testing model tables

65
Zend Framework 2 Documentation, Release 2.2.5

public function testCanDeleteAnAlbumByItsId()
{
$mockTableGateway = $this->getMock(
’ZendDbTableGatewayTableGateway’,
array(’delete’),
array(),
’’,
false
);
$mockTableGateway->expects($this->once())
->method(’delete’)
->with(array(’id’ => 123));
$albumTable = new AlbumTable($mockTableGateway);
$albumTable->deleteAlbum(123);
}
public function testSaveAlbumWillInsertNewAlbumsIfTheyDontAlreadyHaveAnId()
{
$albumData = array(
’artist’ => ’The Military Wives’,
’title’ => ’In My Dreams’
);
$album
= new Album();
$album->exchangeArray($albumData);
$mockTableGateway = $this->getMock(
’ZendDbTableGatewayTableGateway’,
array(’insert’),
array(),
’’,
false
);
$mockTableGateway->expects($this->once())
->method(’insert’)
->with($albumData);
$albumTable = new AlbumTable($mockTableGateway);
$albumTable->saveAlbum($album);
}
public function testSaveAlbumWillUpdateExistingAlbumsIfTheyAlreadyHaveAnId()
{
$albumData = array(
’id’
=> 123,
’artist’ => ’The Military Wives’,
’title’ => ’In My Dreams’,
);
$album
= new Album();
$album->exchangeArray($albumData);
$resultSet = new ResultSet();
$resultSet->setArrayObjectPrototype(new Album());
$resultSet->initialize(array($album));
$mockTableGateway = $this->getMock(
’ZendDbTableGatewayTableGateway’,
array(’select’, ’update’),

66

Chapter 14. Unit Testing a Zend Framework 2 application
Zend Framework 2 Documentation, Release 2.2.5

array(),
’’,
false
);
$mockTableGateway->expects($this->once())
->method(’select’)
->with(array(’id’ => 123))
->will($this->returnValue($resultSet));
$mockTableGateway->expects($this->once())
->method(’update’)
->with(
array(
’artist’ => ’The Military Wives’,
’title’ => ’In My Dreams’
),
array(’id’ => 123)
);
$albumTable = new AlbumTable($mockTableGateway);
$albumTable->saveAlbum($album);
}
public function testExceptionIsThrownWhenGettingNonExistentAlbum()
{
$resultSet = new ResultSet();
$resultSet->setArrayObjectPrototype(new Album());
$resultSet->initialize(array());
$mockTableGateway = $this->getMock(
’ZendDbTableGatewayTableGateway’,
array(’select’),
array(),
’’,
false
);
$mockTableGateway->expects($this->once())
->method(’select’)
->with(array(’id’ => 123))
->will($this->returnValue($resultSet));
$albumTable = new AlbumTable($mockTableGateway);
try {
$albumTable->getAlbum(123);
}
catch (Exception $e) {
$this->assertSame(’Could not find row 123’, $e->getMessage());
return;
}
$this->fail(’Expected exception was not thrown’);
}

These tests are nothing complicated and they should be self explanatory. In each test we are injecting a mock table
gateway into our AlbumTable and set our expectations accordingly.
We are testing that:
1. We can retrieve an individual album by its ID.

14.8. Testing model tables

67
Zend Framework 2 Documentation, Release 2.2.5

2. We can delete albums.
3. We can save new album.
4. We can update existing albums.
5. We will encounter an exception if we’re trying to retrieve an album that doesn’t exist.
Running phpunit command for one last time, we get the output as follows:
PHPUnit 3.7.13 by Sebastian Bergmann.
Configuration read from /var/www/zf2-tutorial/module/Album/test/phpunit.xml
.............
Time: 0 seconds, Memory: 11.50Mb
OK (13 tests, 34 assertions)

14.9 Conclusion
In this short tutorial we gave a few examples how different parts of a Zend Framework 2 MVC application can be
tested. We covered setting up the environment for testing, how to test controllers and actions, how to approach failing
test cases, how to configure the service manager, as well as how to test model entities and model tables.
This tutorial is by no means a definitive guide to writing unit tests, just a small stepping stone helping you develop
applications of higher quality.

68

Chapter 14. Unit Testing a Zend Framework 2 application
CHAPTER 15

Using the EventManager

This tutorial explores the various features of ZendEventManager.

15.1 Terminology
• An Event is a named action.
• A Listener is any PHP callback that reacts to an event.
• An EventManager aggregates listeners for one or more named events, and triggers events.
Typically, an event will be modeled as an object, containing metadata surrounding when and how it was triggered,
including the event name, what object triggered the event (the “target”), and what parameters were provided. Events
are named, which allows a single listener to branch logic based on the event.

15.2 Getting started
The minimal things necessary to start using events are:
• An EventManager instance
• One or more listeners on one or more events
• A call to trigger() an event
The simplest example looks something like this:
1

use ZendEventManagerEventManager;

2
3
4
5
6
7
8
9
10
11
12

$events = new EventManager();
$events->attach(’do’, function ($e) {
$event = $e->getName();
$params = $e->getParams();
printf(
’Handled event "%s", with parameters %s’,
$event,
json_encode($params)
);
});

69
Zend Framework 2 Documentation, Release 2.2.5

13
14
15

$params = array(’foo’ => ’bar’, ’baz’ => ’bat’);
$events->trigger(’do’, null, $params);

The above will result in the following:
Handled event "do", with parameters {"foo":"bar","baz":"bat"}

Note: Throughout this tutorial, we use closures as listeners. However, any valid PHP callback can be attached as a
listeners: PHP function names, static class methods, object instance methods, functors, or closures. We use closures
within this post simply for illustration and simplicity.
If you were paying attention to the example, you will have noted the null argument. Why is it there?
Typically, you will compose an EventManager within a class, to allow triggering actions within methods. The
middle argument to trigger() is the “target”, and in the case described, would be the current object instance. This
gives event listeners access to the calling object, which can often be useful.
1
2
3

use ZendEventManagerEventManager;
use ZendEventManagerEventManagerAwareInterface;
use ZendEventManagerEventManagerInterface;

4
5
6
7

class Example implements EventManagerAwareInterface
{
protected $events;

8

public function setEventManager(EventManagerInterface $events)
{
$events->setIdentifiers(array(
__CLASS__,
get_class($this)
));
$this->events = $events;
}

9
10
11
12
13
14
15
16
17

public function getEventManager()
{
if (!$this->events) {
$this->setEventManager(new EventManager());
}
return $this->events;
}

18
19
20
21
22
23
24
25

public function do($foo, $baz)
{
$params = compact(’foo’, ’baz’);
$this->getEventManager()->trigger(__FUNCTION__, $this, $params);
}

26
27
28
29
30
31
32

}

33
34

$example = new Example();

35
36
37
38
39
40

$example->getEventManager()->attach(’do’, function($e) {
$event = $e->getName();
$target = get_class($e->getTarget()); // "Example"
$params = $e->getParams();
printf(

70

Chapter 15. Using the EventManager
Zend Framework 2 Documentation, Release 2.2.5

’Handled event "%s" on target "%s", with parameters %s’,
$event,
$target,
json_encode($params)

41
42
43
44

);

45
46

});

47
48

$example->do(’bar’, ’bat’);

The above is basically the same as the first example. The main difference is that we’re now using that middle argument in order to pass the target, the instance of Example, on to the listeners. Our listener is now retrieving that
($e->getTarget()), and doing something with it.
If you’re reading this critically, you should have a new question: What is the call to setIdentifiers() for?

15.3 Shared managers
One aspect that the EventManager implementation
SharedEventManagerInterface implementation.

provides

is

an

ability

to

compose

a

ZendEventManagerSharedEventManagerInterface describes an object that aggregates listeners for
events attached to objects with specific identifiers. It does not trigger events itself. Instead, an EventManager instance that composes a SharedEventManager will query the SharedEventManager for listeners on identifiers
it’s interested in, and trigger those listeners as well.
How does this work, exactly?
Consider the following:
1

use ZendEventManagerSharedEventManager;

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

$sharedEvents = new SharedEventManager();
$sharedEvents->attach(’Example’, ’do’, function ($e) {
$event = $e->getName();
$target = get_class($e->getTarget()); // "Example"
$params = $e->getParams();
printf(
’Handled event "%s" on target "%s", with parameters %s’,
$event,
$target,
json_encode($params)
);
});

This looks almost identical to the previous example; the key difference is that there is an additional argument at the
start of the list, ’Example’. This code is basically saying, “Listen to the ‘do’ event of the ‘Example’ target, and,
when notified, execute this callback.”
This is where the setIdentifiers() argument of EventManager comes into play. The method allows passing
a string, or an array of strings, defining the name or names of the context or targets the given instance will be interested
in. If an array is given, then any listener on any of the targets given will be notified.
So, getting back to our example, let’s assume that the above shared listener is registered, and also that the Example
class is defined as above. We can then execute the following:
1
2
3

$example = new Example();
$example->getEventManager()->setSharedManager($sharedEvents);
$example->do(’bar’, ’bat’);

15.3. Shared managers

71
Zend Framework 2 Documentation, Release 2.2.5

and expect the following to be echo‘d:
Handled event "do" on target "Example", with parameters {"foo":"bar","baz":"bat"}

Now, let’s say we extended Example as follows:
1
2
3

class SubExample extends Example
{
}

One interesting aspect of our setEventManager() method is that we defined it to listen both on __CLASS__
and get_class($this). This means that calling do() on our SubExample class would also trigger the shared
listener! It also means that, if desired, we could attach to specifically SubExample, and listeners attached to only
the Example target would not be triggered.
Finally, the names used as contexts or targets need not be class names; they can be some name that only has meaning
in your application if desired. As an example, you could have a set of classes that respond to “log” or “cache” – and
listeners on these would be notified by any of them.
Note: We recommend using class names, interface names, and/or abstract class names for identifiers. This makes
determining what events are available easier, as well as finding which listeners might be attaching to those events.
Interfaces make a particularly good use case, as they allow attaching to a group of related classes a single operation.
At any point, if you do not want to notify shared listeners, pass a null value to setSharedManager():
$events->setSharedManager(null);

and they will be ignored. If at any point, you want to enable them again, pass the SharedEventManager instance:
$events->setSharedManager($sharedEvents);

72

Chapter 15. Using the EventManager
CHAPTER 16

Wildcards

So far, with both a normal EventManager instance and with the SharedEventManager instance, we’ve seen
the usage of singular strings representing the event and target names to which we want to attach. What if you want to
attach a listener to multiple events or targets?
The answer is to supply an array of events or targets, or a wildcard, *.
Consider the following examples:
1
2
3
4
5

// Multiple named events:
$events->attach(
array(’foo’, ’bar’, ’baz’), // events
$listener
);

6
7
8
9
10
11

// All events via wildcard:
$events->attach(
’*’, // all events
$listener
);

12
13
14
15
16
17
18

// Multiple named targets:
$sharedEvents->attach(
array(’Foo’, ’Bar’, ’Baz’), // targets
’doSomething’, // named event
$listener
);

19
20
21
22
23
24
25

// All targets via wildcard
$sharedEvents->attach(
’*’, // all targets
’doSomething’, // named event
$listener
);

26
27
28
29
30
31
32

// Mix and match: multiple named events on multiple named targets:
$sharedEvents->attach(
array(’Foo’, ’Bar’, ’Baz’), // targets
array(’foo’, ’bar’, ’baz’), // events
$listener
);

73
Zend Framework 2 Documentation, Release 2.2.5

33
34
35
36
37
38
39

// Mix and match: all events on multiple named targets:
$sharedEvents->attach(
array(’Foo’, ’Bar’, ’Baz’), // targets
’*’, // events
$listener
);

40
41
42
43
44
45
46

// Mix and match: multiple named events on all targets:
$sharedEvents->attach(
’*’, // targets
array(’foo’, ’bar’, ’baz’), // events
$listener
);

47
48
49
50
51
52
53

// Mix and match: all events on all targets:
$sharedEvents->attach(
’*’, // targets
’*’, // events
$listener
);

The ability to specify multiple targets and/or events when attaching can slim down your code immensely.

74

Chapter 16. Wildcards
CHAPTER 17

Listener aggregates

Another approach to listening to multiple events is via a concept of listener aggregates, represented by
ZendEventManagerListenerAggregateInterface. Via this approach, a single class can listen to multiple events, attaching one or more instance methods as listeners.
This
interface
defines
two
methods,
attach(EventManagerInterface $events)
and
detach(EventManagerInterface $events). Basically, you pass an EventManager instance to
one and/or the other, and then it’s up to the implementing class to determine what to do.
As an example:
1
2
3
4

use
use
use
use

ZendEventManagerEventInterface;
ZendEventManagerEventManagerInterface;
ZendEventManagerListenerAggregateInterface;
ZendLogLogger;

5
6
7
8
9

class LogEvents implements ListenerAggregateInterface
{
protected $listeners = array();
protected $log;

10
11
12
13
14

public function __construct(Logger $log)
{
$this->log = $log;
}

15
16
17
18
19
20

public function attach(EventManagerInterface $events)
{
$this->listeners[] = $events->attach(’do’, array($this, ’log’));
$this->listeners[] = $events->attach(’doSomethingElse’, array($this, ’log’));
}

21
22
23
24
25
26
27
28
29

public function detach(EventCollection $events)
{
foreach ($this->listeners as $index => $listener) {
if ($events->detach($listener)) {
unset($this->listeners[$index];
}
}
}

30

75
Zend Framework 2 Documentation, Release 2.2.5

public function log(EventInterface $e)
{
$event = $e->getName();
$params = $e->getParams();
$this->log->info(sprintf(’%s: %s’, $event, json_encode($params)));
}

31
32
33
34
35
36
37

}

You can attach this using either attach() or attachAggregate():
$logListener = new LogEvents($logger);
$events->attachAggregate($logListener); // OR
$events->attach($logListener);

Any events the aggregate attaches to will then be notified when triggered.
Why bother? For a couple of reasons:
• Aggregates allow you to have stateful listeners. The above example demonstrates this via the composition of
the logger; another example would be tracking configuration options.
• Aggregates make detaching listeners easier.
When you call attach() normally, you receive a
ZendStdlibCallbackHandler instance; the only way to detach() a listener is to pass that instance
back – which means if you want to detach later, you need to keep that instance somewhare. Aggregates typically
do this for you – as you can see in the example above.

17.1 Introspecting results
Sometimes you’ll want to know what your listeners returned. One thing to remember is that you may have multiple
listeners on the same event; the interface for results must be consistent regardless of the number of listeners.
The EventManager implementation by default returns a ZendEventManagerResponseCollection instance. This class extends PHP’s SplStack, allowing you to loop through responses in reverse order (since the last
one executed is likely the one you’re most interested in). It also implements the following methods:
• first() will retrieve the first result received
• last() will retrieve the last result received
• contains($value) allows you to test all values to see if a given one was received, and returns simply a
boolean true if found, and false if not.
Typically, you should not worry about the return values from events, as the object triggering the event shouldn’t really
have much insight into what listeners are attached. However, sometimes you may want to short-circuit execution if
interesting results are obtained.

17.2 Short-ciruiting listener execution
You may want to short-ciruit execution if a particular result is obtained, or if a listener determines that something is
wrong, or that it can return something quicker than the target.
As examples, one rationale for adding an EventManager is as a caching mechanism. You can trigger one event
early in the method, returning if a cache is found, and trigger another event late in the method, seeding the cache.
The EventManager component offers two ways to handle this. The first is to pass a callback as the last argument
to trigger(); if that callback returns a boolean true, execution is halted.
76

Chapter 17. Listener aggregates
Zend Framework 2 Documentation, Release 2.2.5

Here’s an example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14

public function someExpensiveCall($criteria1, $criteria2)
{
$params = compact(’criteria1’, ’criteria2’);
$results = $this->getEventManager()->trigger(
__FUNCTION__,
$this,
$params,
function ($r) {
return ($r instanceof SomeResultClass);
}
);
if ($results->stopped()) {
return $results->last();
}

15

// ... do some work ...

16
17

}

With this paradigm, we know that the likely reason of execution halting is due to the last result meeting the test callback
criteria; as such, we simply return that last result.
The other way to halt execution is within a listener, acting on the Event object it receives. In this case, the listener calls
stopPropagation(true), and the EventManager will then return without notifying any additional listeners.
1
2
3
4

$events->attach(’do’, function ($e) {
$e->stopPropagation();
return new SomeResultClass();
});

This, of course, raises some ambiguity when using the trigger paradigm, as you can no longer be certain that the last
result meets the criteria it’s searching on. As such, we recommend that you standardize on one approach or the other.

17.3 Keeping it in order
On occasion, you may be concerned about the order in which listeners execute. As an example, you may want to do
any logging early, to ensure that if short-circuiting occurs, you’ve logged; or if implementing a cache, you may want
to return early if a cache hit is found, and execute late when saving to a cache.
Each of EventManager::attach() and SharedEentManager::attach() accept one additional argument, a priority. By default, if this is omitted, listeners get a priority of 1, and are executed in the order in which
they are attached. However, if you provide a priority value, you can influence order of execution.
• Higher priority values execute earlier.
• Lower (negative) priority values execute later.
To borrow an example from earlier:
1
2
3
4
5
6
7
8

$priority = 100;
$events->attach(’Example’, ’do’, function($e) {
$event = $e->getName();
$target = get_class($e->getTarget()); // "Example"
$params = $e->getParams();
printf(
’Handled event "%s" on target "%s", with parameters %s’,
$event,

17.3. Keeping it in order

77
Zend Framework 2 Documentation, Release 2.2.5

$target,
json_encode($params)

9
10
11
12

);
}, $priority);

This would execute with high priority, meaning it would execute early. If we changed $priority to -100, it would
execute with low priority, executing late.
While you can’t necessarily know all the listeners attached, chances are you can make adequate guesses when necessary in order to set appropriate priority values. We advise avoiding setting a priority value unless absolutely necessary.

17.4 Custom event objects
Hopefully some of you have been wondering, “where and when is the Event object created”? In all of the examples above, it’s created based on the arguments passed to trigger() – the event name, target, and parameters.
Sometimes, however, you may want greater control over the object.
As an example, one thing that looks like a code smell is when you have code like this:
1
2
3
4

$routeMatch = $e->getParam(’route-match’, false);
if (!$routeMatch) {
// Oh noes! we cannot do our work! whatever shall we do?!?!?!
}

The problems with this are several. First, relying on string keys is going to very quickly run into problems – typos
when setting or retrieving the argument can lead to hard to debug situations. Second, we now have a documentation
issue; how do we document expected arguments? how do we document what we’re shoving into the event? Third, as
a side effect, we can’t use IDE or editor hinting support – string keys give these tools nothing to work with.
Similarly, consider how you might represent a computational result of a method when triggering an event. As an
example:
1
2
3

// in the method:
$params[’__RESULT’] = $computedResult;
$events->trigger(__FUNCTION__ . ’.post’, $this, $params);

4
5
6
7
8
9

// in the listener:
$result = $e->getParam(’__RESULT__’);
if (!$result) {
// Oh noes! we cannot do our work! whatever shall we do?!?!?!
}

Sure, that key may be unique, but it suffers from a lot of the same issues.
So, the solution is to create custom events. As an example, we have a custom MvcEvent in the ZF2 MVC layer. This
event composes the application instance, the router, the route match object, request and response objects, the view
model, and also a result. We end up with code like this in our listeners:
1
2
3
4
5
6

$response = $e->getResponse();
$result
= $e->getResult();
if (is_string($result)) {
$content = $view->render(’layout.phtml’, array(’content’ => $result));
$response->setContent($content);
}

But how do we use this custom event? Simple: trigger() can accept an event object instead of any of the event
name, target, or params arguments.

78

Chapter 17. Listener aggregates
Zend Framework 2 Documentation, Release 2.2.5

1
2

$event = new CustomEvent();
$event->setSomeKey($value);

3
4
5

// Injected with event name and target:
$events->trigger(’foo’, $this, $event);

6
7
8
9

// Injected with event name:
$event->setTarget($this);
$events->trigger(’foo’, $event);

10
11
12
13
14

// Fully encapsulates all necessary properties:
$event->setName(’foo’);
$event->setTarget($this);
$events->trigger($event);

15
16
17
18

// Passing a callback following the event object works for
// short-circuiting, too.
$results = $events->trigger(’foo’, $this, $event, $callback);

This is a really powerful technique for domain-specific event systems, and definitely worth experimenting with.

17.5 Putting it together: Implementing a simple caching system
In previous sections, I indicated that short-circuiting is a way to potentially implement a caching solution. Let’s create
a full example.
First, let’s define a method that could use caching. You’ll note that in most of the examples, I’ve used __FUNCTION__
as the event name; this is a good practice, as it makes it simple to create a macro for triggering events, as well as helps
to keep event names unique (as they’re usually within the context of the triggering class). However, in the case of a
caching example, this would lead to identical events being triggered. As such, I recommend postfixing the event name
with semantic names: “do.pre”, “do.post”, “do.error”, etc. I’ll use that convention in this example.
Additionally, you’ll notice that the $params I pass to the event is usually the list of parameters passed to the method.
This is because those are often not stored in the object, and also to ensure the listeners have the exact same context
as the calling method. But it raises an interesting problem in this example: what name do we give the result of the
method? One standard that has emerged is the use of __RESULT__, as double-underscored variables are typically
reserved for the sytem.
Here’s what the method will look like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14

public function someExpensiveCall($criteria1, $criteria2)
{
$params = compact(’criteria1’, ’criteria2’);
$results = $this->getEventManager()->trigger(
__FUNCTION__ . ’.pre’,
$this,
$params,
function ($r) {
return ($r instanceof SomeResultClass);
}
);
if ($results->stopped()) {
return $results->last();
}

15
16

// ... do some work ...

17.5. Putting it together: Implementing a simple caching system

79
Zend Framework 2 Documentation, Release 2.2.5

17

$params[’__RESULT__’] = $calculatedResult;
$this->events()->trigger(__FUNCTION__ . ’.post’, $this, $params);
return $calculatedResult;

18
19
20
21

}

Now, to provide some caching listeners. We’ll need to attach to each of the “someExpensiveCall.pre” and “someExpensiveCall.post” methods. In the former case, if a cache hit is detected, we return it, and move on. In the latter, we
store the value in the cache.
We’ll assume $cache is defined, and follows the paradigms of ZendCache. We’ll want to return early if a hit
is detected, and execute late when saving a cache (in case the result is modified by another listener). As such, we’ll
set the “someExpensiveCall.pre” listener to execute with priority 100, and the “someExpensiveCall.post” listener to
execute with priority -100.
1
2
3
4
5
6

$events->attach(’someExpensiveCall.pre’, function($e) use ($cache) {
$params = $e->getParams();
$key
= md5(json_encode($params));
$hit
= $cache->load($key);
return $hit;
}, 100);

7
8
9
10
11
12
13
14

$events->attach(’someExpensiveCall.post’, function($e) use ($cache) {
$params = $e->getParams();
$result = $params[’__RESULT__’];
unset($params[’__RESULT__’]);
$key
= md5(json_encode($params));
$cache->save($result, $key);
}, -100);

Note: The above could have been done within a ListenerAggregate, which would have allowed keeping the
$cache instance as a stateful property, instead of importing it into closures.
Another approach would be to move the body of the method to a listener as well, which would allow using the priority
system in order to implement caching. That would look like this:
1
2
3
4
5
6

public function setEventManager(EventManagerInterface $events)
{
$this->events = $events;
$events->setIdentifiers(array(__CLASS__, get_class($this)));
$events->attach(’someExpensiveCall’, array($this, ’doSomeExpensiveCall’));
}

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

public function someExpensiveCall($criteria1, $criteria2)
{
$params = compact(’criteria1’, ’criteria2’);
$results = $this->getEventManager()->trigger(
__FUNCTION__,
$this,
$params,
function ($r) {
return ($r instanceof SomeResultClass);
}
);
return $results->last();
}

21

80

Chapter 17. Listener aggregates
Zend Framework 2 Documentation, Release 2.2.5

22
23
24
25
26
27

public function doSomeExpensiveCall($e)
{
// ... do some work ...
$e->setParam(’__RESULT__’, $calculatedResult);
return $calculatedResult;
}

The listeners would then attach to the “someExpensiveCall” event, with the cache lookup listener listening at high
priority, and the cache storage listener listening at low (negative) priority.
Sure, we could probably simply add caching to the object itself - but this approach allows the same handlers to be
attached to multiple events, or to attach multiple listeners to the same events (e.g. an argument validator, a logger and
a cache manager). The point is that if you design your object with events in mind, you can easily make it more flexible
and extensible, without requiring developers to actually extend it – they can simply attach listeners.

17.6 Conclusion
The EventManager is a powerful component. It drives the workflow of the MVC layer, and is used in countless
components to provide hook points for developers to manipulate the workflow. It can be put to any number of uses
inside your own code, and is an important part of your Zend Framework toolbox.

17.6. Conclusion

81
Zend Framework 2 Documentation, Release 2.2.5

82

Chapter 17. Listener aggregates
CHAPTER 18

Advanced Configuration Tricks

Configuration of Zend Framework 2 applications happens in several steps:
• Initial configuration is passed to the Application instance and used to seed the ModuleManager and
ServiceManager. In this tutorial, we will call this configuration system configuration.
• The ModuleManager‘s ConfigListener aggregates configuration and merges it while modules are being
loaded. In this tutorial, we will call this configuration application configuration.
• Once configuration is aggregated from all modules, the ConfigListener will also merge application configuration globbed in specified directories (typically config/autoload/).
In this tutorial, we’ll look at the exact sequence, and how you can tie into it.

18.1 System configuration
To begin module loading, we have to tell the Application instance about the available modules and where
they live, optionally provide some information to the default module listeners (e.g., where application configuration lives, and what files to load; whether to cache merged configuration, and where; etc.), and optionally seed the
ServiceManager. For purposes of this tutorial we will call this the system configuration.
When
using
the
skeleton
application,
the
system
configuration
config/application.config.php. The defaults look like this:
1
2
3
4
5
6

is

by

default

in

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

7
8
9
10
11
12
13
14
15
16

// 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’,

83
Zend Framework 2 Documentation, Release 2.2.5

),

17
18

// An array of paths from which to glob configuration files after
// modules are loaded. These effectively overide configuration
// provided by modules themselves. Paths may use GLOB_BRACE notation.
’config_glob_paths’ => array(
’config/autoload/{,*.}{global,local}.php’,
),

19
20
21
22
23
24
25

// Whether or not to enable a configuration cache.
// If enabled, the merged configuration will be cached and used in
// subsequent requests.
//’config_cache_enabled’ => $booleanValue,

26
27
28
29
30

// The key used to create the configuration cache file name.
//’config_cache_key’ => $stringKey,

31
32
33

// Whether or not to enable a module class map cache.
// If enabled, creates a module class map cache which will be used
// by in future requests, to reduce the autoloading process.
//’module_map_cache_enabled’ => $booleanValue,

34
35
36
37
38

// The key used to create the class map cache file name.
//’module_map_cache_key’ => $stringKey,

39
40
41

// The path in which to cache merged configuration.
//’cache_dir’ => $stringPath,

42
43
44

//
//
//
//

45
46
47
48

Whether or not to enable modules dependency checking.
Enabled by default, prevents usage of modules that depend on other modules
that weren’t loaded.
’check_dependencies’ => true,

),

49
50

// Used to create an own service manager. May contain one or more child arrays.
//’service_listener_options’ => array(
//
array(
//
’service_manager’ => $stringServiceManagerName,
//
’config_key’
=> $stringConfigKey,
//
’interface’
=> $stringOptionalInterface,
//
’method’
=> $stringRequiredMethodName,
//
),
// )

51
52
53
54
55
56
57
58
59
60

// Initial configuration with which to seed the ServiceManager.
// Should be compatible with ZendServiceManagerConfig.
// ’service_manager’ => array(),

61
62
63
64

);

The system configuration is for the bits and pieces related to the MVC that run before your application is ready. The
configuration is usually brief, and quite minimal.
Also, system configuration is used immediately, and is not merged with any other configuration – which means it
cannot be overridden by a module.
This leads us to our first trick: how do you provide environment-specific system configuration?

84

Chapter 18. Advanced Configuration Tricks
Zend Framework 2 Documentation, Release 2.2.5

18.1.1 Environment-specific system configuration
What happens when you want to change the set of modules you use based on the environment? Or if the configuration
caching should be enabled based on environment?
It is for this reason that the default system configuration we provide in the skeleton application is in PHP; providing it
in PHP means you can programmatically manipulate it.
As an example, let’s make the following requirements:
• We want to use the ZendDeveloperTools module in development only.
• We want to have configuration caching on in production only.
To make this happen, we’ll set an environment variable in our web server configuration, APP_ENV. In Apache, you’d
put a directive like the following in either your system-wide apache.conf or httpd.conf, or in the definition
for your virtual host; alternately, it can be placed in an .htaccess file.
SetEnv "APP_ENV" "development"

For other web servers, consult the web server documentation to determine how to set environment variables.
To simplify matters, we’ll assume the environment is “production” if no environment variable is present.
We’ll modify the config/application.config.php file to read as follows:
1
2

<?php
$env = getenv(’APP_ENV’) ?: ’production’;

3
4
5
6
7
8
9
10

// Use the $env value to determine which modules to load
$modules = array(
’Application’,
);
if ($env == ’development’) {
$modules[] = ’ZendDeveloperTools’;
}

11
12
13

return array(
’modules’ => $modules,

14
15
16
17
18
19

’module_listener_options’ => array(
’module_paths’ => array(
’./module’,
’./vendor’,
),

20
21
22
23

’config_glob_paths’ => array(
’config/autoload/{,*.}{global,local}.php’,
),

24
25
26

// Use the $env value to determine the state of the flag
’config_cache_enabled’ => ($env == ’production’),

27
28

’config_cache_key’ => ’app_config’,

29
30
31

// Use the $env value to determine the state of the flag
’module_map_cache_enabled’ => ($env == ’production’),

32
33

’module_map_cache_key’ => ’module_map’,

34
35

’cache_dir’ => ’data/config/’,

18.1. System configuration

85
Zend Framework 2 Documentation, Release 2.2.5

36

// Use the $env value to determine the state of the flag
’check_dependencies’ => ($env != ’production’),

37
38

),

39
40

);

This approach gives you flexibility to alter system-level settings.
However, how about altering application specific settings (not system configuration) based on the environment?

18.1.2 Environment-specific application configuration
Sometimes you want to change application configuration to load things such as database adapters, log
writers, cache adapters, and more based on the environment. These are typically managed in the service manager, and may be defined by modules.
You can override them at the application level via
ZendModuleManagerListenerConfigListener, by specifying a glob path in the system configuration – the module_listener_options.config_glob_paths key from the previous examples.
The default value for this is config/autoload/{,*.}{global,local}.php. What this means is that it will
look for application configuration files in the config/autoload directory, in the following order:
• global.php
• *.global.php
• local.php
• *.local.php
This allows you to define application-level defaults in “global” configuration files, which you would then commit to
your version control system, and environment-specific overrides in your “local” configuration files, which you would
omit from version control.
This is a great solution for development, as it allows you to specify alternate configuration that’s specific to your development environment without worrying about accidently deploying it. However, what if you have more environments
– such as a “testing” or “staging” environment – and they each have their own specific overrides?
Again, the application environment variable comes to play. We can alter the glob path in the system configuration
slightly:
’config_glob_paths’ => array(
sprintf(’config/autoload/{,*.}{global,%s,local}.php’, $env)
),

The above will allow you to define an additional set of application configuration files per environment; furthermore,
these will be loaded only if that environment is detected!
As an example, consider the following tree of configuration files:
config/
autoload/
global.php
local.php
users.development.php
users.testing.php
users.local.php

If $env evaluates to testing, then the following files will be merged, in the following order:

86

Chapter 18. Advanced Configuration Tricks
Zend Framework 2 Documentation, Release 2.2.5

global.php
users.testing.php
local.php
users.local.php

Note that users.development.php is not loaded – this is because it will not match the glob pattern!
Also, because of the order in which they are loaded, you can predict which values will overwrite the others, allowing
you to both selectively overwrite as well as debug later.
Note: The files under config/autoload/ are merged after your module configuration, detailed in next section.
We have detailed it here, however, as setting up the application configuration glob path happens within the system
configuration (config/application.config.php).

18.2 Module Configuration
One responsibility of modules is to provide their own configuration to the application. Modules have two general
mechanisms for doing this.
First, modules that either implement ZendModuleManagerFeatureConfigProviderInterface
and/or a getConfig() method can return their configuration. The default, recommended implementation of the
getConfig() method is:
public function getConfig()
{
return include __DIR__ . ’/config/module.config.php’;
}

where module.config.php returns a PHP array. From that PHP array you can provide general configuration
as well as configuration for all the available Manager classes provided by the ServiceManager. Please refer to the
Configuration mapping table to see which configuration key is used for each specific Manager.
Second, modules can implement a number of interfaces and/or methods related to specific service manager or plugin
manager configuration. You will find an overview of all interfaces and their matching Module Configuration functions
inside the Configuration mapping table.
All interfaces are in the ZendModuleManagerFeature namespace, and each is expected to return an array of
configuration for a service manager, as denoted in the section on default service configuration.

18.2. Module Configuration

87
Zend Framework 2 Documentation, Release 2.2.5

18.3 Configuration mapping table
Manager name
Interface name
Module Method name
Config key name
ControllerPluginManager
ControllerPluginProviderInterface
getControllerPluginConfig()
controller_plugins
ControllerLoader
ControllerProviderInterface
getControllerConfig()
controllers
FilterManager
FilterProviderInterface getFilterConfig()
filters
FormElementManager FormElementProviderInterface
getFormElementConfig()
form_elements
HydratorManager
HydratorProviderInterfacegetHydratorConfig() hydrators
InputFilterManager InputFilterProviderInterface
getInputFilterConfig()
input_filters
RoutePluginManager RouteProviderInterface
getRouteConfig()
route_manager
SerializerAdapterManager
SerializerProviderInterface
getSerializerConfig()
serializers
ServiceLocator
ServiceProviderInterface getServiceConfig()
service_manager
ValidatorManager
ValidatorProviderInterface
getValidatorConfig() validators
ViewHelperManager ViewHelperProviderInterface
getViewHelperConfig()
view_helpers

18.4 Configuration Priority
Considering that you may have service configuration in your module configuration file, what has precedence?
The order in which they are merged is:
• configuration returned by getConfig()
• configuration returned by the various service configuration methods in a module class
In other words, your various service configuration methods win. Additionally, and of particular note: the configuration
returned from those methods will not be cached. The reason for this is that it is not uncommon to use closures or factory
instances in configuration returned from your Module class – which cannot be cached reliably.
Note: Use the various service configuration methods when you need to define closures or instance callbacks for
factories, abstract factories, and initializers. This prevents caching problems, and also allows you to write your configuration files in other markup formats.

18.5 Configuration merging workflow
To cap off the tutorial, let’s review how and when configuration is defined and merged.
• System configuration
– Defined in config/application.config.php
– No merging occurs
– Allows manipulation programmatically, which allows the ability to:
* Alter flags based on computed values
* Alter the configuration glob path based on computed values
– Configuration is passed to the Application instance, and then the ModuleManager in order to initialize the system.
• Application configuration
– The ModuleManager loops through each module class in the order defined in the system configuration
88

Chapter 18. Advanced Configuration Tricks
Zend Framework 2 Documentation, Release 2.2.5

* Service configuration defined in Module class methods is aggregated
* Configuration returned by Module::getConfig() is aggregated
– Files detected from the service configuration config_glob_paths setting are merged, based on the
order they resolve in the glob path.
– Merged configuration is finally passed to the ServiceManager

18.5. Configuration merging workflow

89
Zend Framework 2 Documentation, Release 2.2.5

90

Chapter 18. Advanced Configuration Tricks
CHAPTER 19

Using ZendNavigation in your Album
Module

In this tutorial we will use the ZendNavigation component to add a navigation menu to the black bar at the top of the
screen, and add breadcrumbs above the main site content.

19.1 Preparation
In a real world application, the album browser would be only a portion of a working website. Usually the user would
land on a homepage first, and be able to view albums by using a standard navigation menu. So that we have a site
that is more realistic than just the albums feature, lets make the standard skeleton welcome page our homepage, with
the /album route still showing our album module. In order to make this change, we need to undo some work we did
earlier. Currently, navigating to the root of your app (/) routes you to the AlbumController‘s default action. Let’s
undo this route change so we have two discrete entry points to the app, a home page, and an albums area.
module/Application/config/module.config.php
1
2
3
4
5
6
7
8
9
10

’home’ => array(
’type’ => ’ZendMvcRouterHttpLiteral’,
’options’ => array(
’route’
=> ’/’,
’defaults’ => array(
’controller’ => ’ApplicationControllerIndex’, // <-- change back here
’action’
=> ’index’,
),
),
),

This change means that if you go to the home page of your application (http://zf2-tutorial.localhost/), you see the
default skeleton application introduction. Your list of albums is still available at the /album route.

19.2 Setting Up ZendNavigation
Firstly, we need to tell our application which NavigationFactory to use when using the bundled navigation view
helpers. Thankfully, ZF2 comes with a default factory that will suit our needs just fine. To tell ZF2 to use this default

91
Zend Framework 2 Documentation, Release 2.2.5

factory, we simply add a navigation key to the service manager. Its best to do this in the Application module,
because, like the translation data, this is specific to the entire application, and not just to our album pages:
module/Application/config/module.config.php
1
2
3
4
5
6

’service_manager’ => array(
’factories’ => array(
’translator’ => ’ZendI18nTranslatorTranslatorServiceFactory’,
’navigation’ => ’ZendNavigationServiceDefaultNavigationFactory’, // <-- add this
),
),

19.3 Configuring our Site Map
Next up, we need ZendNavigation to understand the hierarchy of our site. Thankfully, if we add a navigation
key to our merged config, the navigation factory will automagically create the container and pages needed to use the
view helpers. Let’s do this in the Application module:
module/Application/config/module.config.php
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

return array(
...
’navigation’ => array(
’default’ => array(
array(
’label’ => ’Home’,
’route’ => ’home’,
),
array(
’label’ => ’Album’,
’route’ => ’album’,
’pages’ => array(
array(
’label’ => ’Add’,
’route’ => ’album’,
’action’ => ’add’,
),
array(
’label’ => ’Edit’,
’route’ => ’album’,
’action’ => ’edit’,
),
array(
’label’ => ’Delete’,
’route’ => ’album’,
’action’ => ’delete’,
),
),
),
),
),
...
);

This configuration maps out the pages we’ve defined in our controller, with labels linking to the given route names. You
can define highly complex hierarchical sites here with pages and sub-pages linking to route names, controller/action
pairs or external uris. For more information see the docs here.

92

Chapter 19. Using ZendNavigation in your Album Module
Zend Framework 2 Documentation, Release 2.2.5

19.4 Adding the Menu View Helper
Now that we have the navigation helper configured by our service manager and merged config, we can easily add the
menu to the title bar to our layout by using the menu view helper:
module/Application/view/layout/layout.phtml
1
2
3
4
5
6
7

...
<a class="brand"
href="<?php echo $this->url(’home’) ?>"><?php echo $this->translate(’Skeleton Application’) ?
<?php // <-- Add this !!
echo $this->navigation(’navigation’)->menu();
?>
...

The navigation helper is built in to Zend Framework 2, and uses the service manager configuration we’ve already
defined to configure itself automatically. Refreshing your application you will see a working menu, with just a few
tweaks however, we can make it look awesome:
module/Application/view/layout/layout.phtml
1
2
3
4
5
6
7
8
9

<a class="brand"
href="<?php echo $this->url(’home’) ?>"><?php echo $this->translate(’Skeleton Application’) ?></a
<?php // <-- Update this !!
echo $this->navigation(’navigation’)
->menu()
->setMinDepth(0)
->setMaxDepth(0)
->setUlClass(’nav navbar-nav’);
?>

Here we tell the renderer to give the root UL the class of ‘nav’ so that Twitter Bootstrap styles the menu correctly, and
only render the first level of any given page. If you view your application in your browser, you will now see a nicely
styled menu appear in the title bar. The great thing about ZendNavigation is that it integrates with ZF2’s route
so can tell which page you are currently viewing. Because of this, it sets the active page to have a class of active in
the menu. Twitter Bootstrap uses this to highlight your current page accordingly.

19.5 Adding Breadcrumbs
Adding breadcrumbs is initially just as simple. In our layout.phtml we want to add breadcrumbs above the main
content pane, so our foolish user knows exactly where they are in our complex website. Inside the container div, before
we output the content from the view, let’s add a simple breadcrumb by using the breadcrumbs view helper:
module/Application/view/layout/layout.phtml
1
2
3
4
5
6

...
<div class="container">
<?php echo $this->navigation(’navigation’)->breadcrumbs()->setMinDepth(0); // <-- Add this!! ?>
<?php echo $this->content; ?>
</div>
...

This adds a simple but functional breadcrumb to every page (we simply tell it to render from a depth of 0 so we see
all level of pages) but we can do better than that! Because Bootstrap has a styled breadcrumb as part of it’s base
CSS, so let’s add a partial that outputs the UL in bootstrap happy CSS. We’ll create it in the view directory of the
Application module (this partial is application wide, rather than album specific):
module/Application/view/partial/breadcrumb.phtml

19.4. Adding the Menu View Helper

93
Zend Framework 2 Documentation, Release 2.2.5

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

<ul class="breadcrumb">
<?php
// iterate through the pages
foreach ($this->pages as $key => $page):
?>
<li>
<?php
// if this isn’t the last page, add a link and the separator
if ($key < count($this->pages) - 1):
?>
<a href="<?php echo $page->getHref(); ?>"><?php echo $page->getLabel(); ?></a>
<span class="divider">/</span>
<?php
// otherwise, just output the name
else:
?>
<?php echo $page->getLabel(); ?>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>

Notice how the partial is passed a ZendViewModelViewModel instance with the pages property set to an
array of pages to render. Now all we have to do is tell the breadcrumb helper to use the partial we have just written:
module/Application/view/layout/layout.phtml
1
2
3
4
5
6
7
8
9
10
11

...
<div class="container">
<?php
echo $this->navigation(’navigation’) // <-- Update this!!
->breadcrumbs()
->setMinDepth(0)
->setPartial(array(’partial/breadcrumb.phtml’, ’Album’));
?>
<?php echo $this->content; ?>
</div>
...

Refreshing the page now gives us a lovely styled set of breadcrumbs on each page.

94

Chapter 19. Using ZendNavigation in your Album Module
CHAPTER 20

Using ZendPaginator in your Album
Module

In this tutorial we will use the ZendPaginator component to add a handy pagination controller to the bottom of the
album list.
Currently, we only have a handful of albums to display, so showing everything on one page is not a problem. However,
how will the album list look when we have 100 albums or more in our database? The standard solution to this problem
is to split the data up into a number of pages, and allow the user to navigate around these pages using a pagination
control. Just type “Zend Framework” into Google, and you can see their pagination control at the bottom of the page:

20.1 Preparation
In order for us to have lots of albums in our database, you’ll need to run the following SQL insert statement to insert
the current 150 top iTunes albums (at the time of writing!):
INSERT INTO ‘album‘ (‘artist‘, ‘title‘)
VALUES
(’David Bowie’, ’The Next Day (Deluxe Version)’),
(’Bastille’, ’Bad Blood’),
(’Bruno Mars’, ’Unorthodox Jukebox’),
(’Emeli Sand’, ’Our Version of Events (Special Edition)’),
(’Bon Jovi’, ’What About Now (Deluxe Version)’),
(’Justin Timberlake’, ’The 20/20 Experience (Deluxe Version)’),
(’Bastille’, ’Bad Blood (The Extended Cut)’),
(’P!nk’, ’The Truth About Love’),
(’Sound City - Real to Reel’, ’Sound City - Real to Reel’),
(’Jake Bugg’, ’Jake Bugg’),
(’Various Artists’, ’The Trevor Nelson Collection’),

95
Zend Framework 2 Documentation, Release 2.2.5

(’David Bowie’, ’The Next Day’),
(’Mumford & Sons’, ’Babel’),
(’The Lumineers’, ’The Lumineers’),
(’Various Artists’, ’Get Ur Freak On - R&B Anthems’),
(’The 1975’, ’Music For Cars EP’),
(’Various Artists’, ’Saturday Night Club Classics - Ministry of Sound’),
(’Hurts’, ’Exile (Deluxe)’),
(’Various Artists’, ’Mixmag - The Greatest Dance Tracks of All Time’),
(’Ben Howard’, ’Every Kingdom’),
(’Stereophonics’, ’Graffiti On the Train’),
(’The Script’, ’#3’),
(’Stornoway’, ’Tales from Terra Firma’),
(’David Bowie’, ’Hunky Dory (Remastered)’),
(’Worship Central’, ’Let It Be Known (Live)’),
(’Ellie Goulding’, ’Halcyon’),
(’Various Artists’, ’Dermot O’Leary Presents the Saturday Sessions 2013’),
(’Stereophonics’, ’Graffiti On the Train (Deluxe Version)’),
(’Dido’, ’Girl Who Got Away (Deluxe)’),
(’Hurts’, ’Exile’),
(’Bruno Mars’, ’Doo-Wops & Hooligans’),
(’Calvin Harris’, ’18 Months’),
(’Olly Murs’, ’Right Place Right Time’),
(’Alt-J (?)’, ’An Awesome Wave’),
(’One Direction’, ’Take Me Home’),
(’Various Artists’, ’Pop Stars’),
(’Various Artists’, ’Now That’s What I Call Music! 83’),
(’John Grant’, ’Pale Green Ghosts’),
(’Paloma Faith’, ’Fall to Grace’),
(’Laura Mvula’, ’Sing To the Moon (Deluxe)’),
(’Duke Dumont’, ’Need U (100%) [feat. A*M*E] - EP’),
(’Watsky’, ’Cardboard Castles’),
(’Blondie’, ’Blondie: Greatest Hits’),
(’Foals’, ’Holy Fire’),
(’Maroon 5’, ’Overexposed’),
(’Bastille’, ’Pompeii (Remixes) - EP’),
(’Imagine Dragons’, ’Hear Me - EP’),
(’Various Artists’, ’100 Hits: 80s Classics’),
(’Various Artists’, ’Les Misrables (Highlights From the Motion Picture Soundtrack)’),
(’Mumford & Sons’, ’Sigh No More’),
(’Frank Ocean’, ’Channel ORANGE’),
(’Bon Jovi’, ’What About Now’),
(’Various Artists’, ’BRIT Awards 2013’),
(’Taylor Swift’, ’Red’),
(’Fleetwood Mac’, ’Fleetwood Mac: Greatest Hits’),
(’David Guetta’, ’Nothing But the Beat Ultimate’),
(’Various Artists’, ’Clubbers Guide 2013 (Mixed By Danny Howard) - Ministry of Sound’),
(’David Bowie’, ’Best of Bowie’),
(’Laura Mvula’, ’Sing To the Moon’),
(’ADELE’, ’21’),
(’Of Monsters and Men’, ’My Head Is an Animal’),
(’Rihanna’, ’Unapologetic’),
(’Various Artists’, ’BBC Radio 1’s Live Lounge - 2012’),
(’Avicii & Nicky Romero’, ’I Could Be the One (Avicii vs. Nicky Romero)’),
(’The Streets’, ’A Grand Don’t Come for Free’),
(’Tim McGraw’, ’Two Lanes of Freedom’),
(’Foo Fighters’, ’Foo Fighters: Greatest Hits’),
(’Various Artists’, ’Now That’s What I Call Running!’),
(’Swedish House Mafia’, ’Until Now’),

96

Chapter 20. Using ZendPaginator in your Album Module
Zend Framework 2 Documentation, Release 2.2.5

(’The xx’, ’Coexist’),
(’Five’, ’Five: Greatest Hits’),
(’Jimi Hendrix’, ’People, Hell & Angels’),
(’Biffy Clyro’, ’Opposites (Deluxe)’),
(’The Smiths’, ’The Sound of the Smiths’),
(’The Saturdays’, ’What About Us - EP’),
(’Fleetwood Mac’, ’Rumours’),
(’Various Artists’, ’The Big Reunion’),
(’Various Artists’, ’Anthems 90s - Ministry of Sound’),
(’The Vaccines’, ’Come of Age’),
(’Nicole Scherzinger’, ’Boomerang (Remixes) - EP’),
(’Bob Marley’, ’Legend (Bonus Track Version)’),
(’Josh Groban’, ’All That Echoes’),
(’Blue’, ’Best of Blue’),
(’Ed Sheeran’, ’+’),
(’Olly Murs’, ’In Case You Didn’t Know (Deluxe Edition)’),
(’Macklemore & Ryan Lewis’, ’The Heist (Deluxe Edition)’),
(’Various Artists’, ’Defected Presents Most Rated Miami 2013’),
(’Gorgon City’, ’Real EP’),
(’Mumford & Sons’, ’Babel (Deluxe Version)’),
(’Various Artists’, ’The Music of Nashville: Season 1, Vol. 1 (Original Soundtrack)’),
(’Various Artists’, ’The Twilight Saga: Breaking Dawn, Pt. 2 (Original Motion Picture Soundtrack)
(’Various Artists’, ’Mum - The Ultimate Mothers Day Collection’),
(’One Direction’, ’Up All Night’),
(’Bon Jovi’, ’Bon Jovi Greatest Hits’),
(’Agnetha Fltskog’, ’A’),
(’Fun.’, ’Some Nights’),
(’Justin Bieber’, ’Believe Acoustic’),
(’Atoms for Peace’, ’Amok’),
(’Justin Timberlake’, ’Justified’),
(’Passenger’, ’All the Little Lights’),
(’Kodaline’, ’The High Hopes EP’),
(’Lana Del Rey’, ’Born to Die’),
(’JAY Z & Kanye West’, ’Watch the Throne (Deluxe Version)’),
(’Biffy Clyro’, ’Opposites’),
(’Various Artists’, ’Return of the 90s’),
(’Gabrielle Aplin’, ’Please Don’t Say You Love Me - EP’),
(’Various Artists’, ’100 Hits - Driving Rock’),
(’Jimi Hendrix’, ’Experience Hendrix - The Best of Jimi Hendrix’),
(’Various Artists’, ’The Workout Mix 2013’),
(’The 1975’, ’Sex’),
(’Chase & Status’, ’No More Idols’),
(’Rihanna’, ’Unapologetic (Deluxe Version)’),
(’The Killers’, ’Battle Born’),
(’Olly Murs’, ’Right Place Right Time (Deluxe Edition)’),
(’A$AP Rocky’, ’LONG.LIVE.A$AP (Deluxe Version)’),
(’Various Artists’, ’Cooking Songs’),
(’Haim’, ’Forever - EP’),
(’Lianne La Havas’, ’Is Your Love Big Enough?’),
(’Michael Bubl’, ’To Be Loved’),
(’Daughter’, ’If You Leave’),
(’The xx’, ’xx’),
(’Eminem’, ’Curtain Call’),
(’Kendrick Lamar’, ’good kid, m.A.A.d city (Deluxe)’),
(’Disclosure’, ’The Face - EP’),
(’Palma Violets’, ’180’),
(’Cody Simpson’, ’Paradise’),
(’Ed Sheeran’, ’+ (Deluxe Version)’),

20.1. Preparation

97
Zend Framework 2 Documentation, Release 2.2.5

(’Michael Bubl’, ’Crazy Love (Hollywood Edition)’),
(’Bon Jovi’, ’Bon Jovi Greatest Hits - The Ultimate Collection’),
(’Rita Ora’, ’Ora’),
(’g33k’, ’Spabby’),
(’Various Artists’, ’Annie Mac Presents 2012’),
(’David Bowie’, ’The Platinum Collection’),
(’Bridgit Mendler’, ’Ready or Not (Remixes) - EP’),
(’Dido’, ’Girl Who Got Away’),
(’Various Artists’, ’Now That’s What I Call Disney’),
(’The 1975’, ’Facedown - EP’),
(’Kodaline’, ’The Kodaline - EP’),
(’Various Artists’, ’100 Hits: Super 70s’),
(’Fred V & Grafix’, ’Goggles - EP’),
(’Biffy Clyro’, ’Only Revolutions (Deluxe Version)’),
(’Train’, ’California 37’),
(’Ben Howard’, ’Every Kingdom (Deluxe Edition)’),
(’Various Artists’, ’Motown Anthems’),
(’Courteeners’, ’ANNA’),
(’Johnny Marr’, ’The Messenger’),
(’Rodriguez’, ’Searching for Sugar Man’),
(’Jessie Ware’, ’Devotion’),
(’Bruno Mars’, ’Unorthodox Jukebox’),
(’Various Artists’, ’Call the Midwife (Music From the TV Series)’
);

This gives us a handy extra 150 rows to play with. If you now visit your album list at /album, you’ll see a huge long
list of 150+ albums, its ugly.

20.2 Modifying the AlbumTable
In order to let ZF2 handle our database queries automatically for us, we will be using the
ZendPaginatorAdapterDbSelect paginator adapter.
This will automatically manipulate and run a
ZendDbSqlSelect object to include the correct LIMIT and WHERE clauses, so that it returns only the
right amount of data needed to display the given page. Let’s modify the fetchAll method of the AlbumTable
model, so that it can optionally return a paginator object:
module/Album/src/Album/Model/AlbumTable.php
1
2

<?php
namespace AlbumModel;

3
4
5
6
7
8

use
use
use
use
use

ZendDbResultSetResultSet;
ZendDbTableGatewayTableGateway;
ZendDbSqlSelect;
ZendPaginatorAdapterDbSelect;
ZendPaginatorPaginator;

9
10
11
12
13
14
15
16
17
18

class AlbumTable
{
...
public function fetchAll($paginated=false)
{
if ($paginated) {
// create a new Select object for the table album
$select = new Select(’album’);
// create a new result set based on the Album entity

98

Chapter 20. Using ZendPaginator in your Album Module
Zend Framework 2 Documentation, Release 2.2.5

$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Album());
// create a new pagination adapter object
$paginatorAdapter = new DbSelect(
// our configured select object
$select,
// the adapter to run it against
$this->tableGateway->getAdapter(),
// the result set to hydrate
$resultSetPrototype
);
$paginator = new Paginator($paginatorAdapter);
return $paginator;

19
20
21
22
23
24
25
26
27
28
29
30
31

}
$resultSet = $this->tableGateway->select();
return $resultSet;

32
33
34

}
...

35
36

This will return a fully configured Paginator object. We’ve already told the DbSelect adapter to use our created
Select object, to use the adapter that the TableGateway object uses, and also how to hydrate the result into a
Album entity in the same fashion as the TableGateway does. This means that our executed and returned paginator
results will return Album objects in exactly the same fashion as the non-paginated results.

20.3 Modifying the AlbumController
Next, we need to tell the album controller to return a Pagination object instead of a ResultSet. Both these
objects can by iterated over to return hydrated Album objects, so we won’t need to make many changes to the view
script:
module/Album/src/Album/Controller/AlbumController.php
1
2
3
4
5
6
7
8
9

...
public function indexAction()
{
// grab the paginator from the AlbumTable
$paginator = $this->getAlbumTable()->fetchAll(true);
// set the current page to what has been passed in query string, or to 1 if none set
$paginator->setCurrentPageNumber((int) $this->params()->fromQuery(’page’, 1));
// set the number of items per page to 10
$paginator->setItemCountPerPage(10);

10

return new ViewModel(array(
’paginator’ => $paginator
));

11
12
13
14
15

}
...

Here we are getting the configured Paginator object from the AlbumTable, and then telling it to use the page
that is optionally passed in the querystring page parameter. We are also telling the paginator we want to display 10
objects per page.

20.4 Updating the View Script
Now, let’s just tell the view script to iterate over the pagination view variable, rather than the albums variable:
20.3. Modifying the AlbumController

99
Zend Framework 2 Documentation, Release 2.2.5

module/Album/view/album/album/index.phtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

<table class="table">
<tr>
<th>Title</th>
<th>Artist</th>
<th>&nbsp;</th>
</tr>
<?php foreach ($this->paginator as $album) : // <-- change here! ?>
<tr>
<td><?php echo $this->escapeHtml($album->title);?></td>
<td><?php echo $this->escapeHtml($album->artist);?></td>
<td>
<a href="<?php echo $this->url(’album’,
array(’action’ => ’edit’, ’id’ => $album->id));?>">Edit</a>
<a href="<?php echo $this->url(’album’,
array(’action’ => ’delete’, ’id’ => $album->id));?>">Delete</a>
</td>
</tr>
<?php endforeach; ?>
</table>

Checking the /album route on your website should now give you a list of just 10 albums, but with no method to
navigate through the pages. Let’s correct that now...

20.5 Creating the Pagination Control Partial
Much like we created a custom breadcrumbs partial to render our breadcrumb in the last tutorial, we need to create
a custom pagination control partial to render our pagination control just the way we want it. Again, because we are
using Twitter Bootstrap, this should be as simple as outputting correctly formatted html to get a pretty control. Let’s
create the partial in the module/Application/view/partial/ folder, so that we can use the control in all our
modules:
module/Application/view/partial/paginator.phtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

<?php if ($this->pageCount): ?>
<div class="pagination pagination-centered">
<ul>
<!-- Previous page link -->
<?php if (isset($this->previous)): ?>
<li>
<a href="<?php echo $this->url($this->route); ?>?page=<?php echo $this->previous
<<
</a>
</li>
<?php else: ?>
<li class="disabled">
<a href="#">
<<
</a>
</li>
<?php endif; ?>

18

<!-- Numbered page links -->
<?php foreach ($this->pagesInRange as $page): ?>
<?php if ($page != $this->current): ?>
<li>

19
20
21
22

100

Chapter 20. Using ZendPaginator in your Album Module
Zend Framework 2 Documentation, Release 2.2.5

23
24
25
26
27
28
29
30
31
32

<a href="<?php echo $this->url($this->route);?>?page=<?php echo $page; ?>">
<?php echo $page; ?>
</a>
</li>
<?php else: ?>
<li class="active">
<a href="#"><?php echo $page; ?></a>
</li>
<?php endif; ?>
<?php endforeach; ?>

33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

<!-- Next page link -->
<?php if (isset($this->next)): ?>
<li>
<a href="<?php echo $this->url($this->route); ?>?page=<?php echo $this->next; ?>
>>
</a>
</li>
<?php else: ?>
<li class="disabled">
<a href="#">
>>
</a>
</li>
<?php endif; ?>
</ul>
</div>
<?php endif; ?>

All this partial does is to create a pagination control with links to the correct pages (if there is more than one page in
the pagination object). It will render a previous page link (and mark it disabled if you are at the first page), then render
a list of intermediate pages (that are passed to the partial based on the rendering style – we’ll set in the view helper in
the next step). Finally, it will create a next page link (and disable it if you’re at the end). Notice how we pass the page
number via the page querystring parameter which we have already told our controller to use to display the current
page.

20.5. Creating the Pagination Control Partial

101
Zend Framework 2 Documentation, Release 2.2.5

102

Chapter 20. Using ZendPaginator in your Album Module
CHAPTER 21

Using the PaginationControl View
Helper

The only thing left for us to do so that we can page through the albums is to use the paginationControl view helper to
display our pagination control. This is nicely straightforward as we have already done all the ground work needed to
display the control:
module/Album/view/album/album/index.phtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

...
<?php
// add at the end of the file after the table
echo $this->paginationControl(
// the paginator object
$this->paginator,
// the scrolling style
’sliding’,
// the partial to use to render the control
array(’partial/paginator.phtml’, ’Album’),
// the route to link to when a user clicks a control link
array(
’route’ => ’album’
)
);
?>

All we need to do here is to echo the paginationControl helper, and tell it to use our paginator object, sliding scrolling
style, our paginator partial, and which route to use for clicks. Refreshing your application should give you a lovely
bootstrap styled pagination control!

103
Zend Framework 2 Documentation, Release 2.2.5

104

Chapter 21. Using the PaginationControl View Helper
CHAPTER 22

Setting up a database adapter

22.1 Introduction
In most cases, e.g. in your controllers, your database adapter can be fetched directly from the service manager. Some
classes however, like ZendValidatorDbRecordExists isn’t aware of the service manager, but still needs an
adapter to function.
There are many different ways to provide this functionality to your application. Below are a few examples.

22.2 Basic setup
Normally you will setup your database adapter using a factory in the service manager in your configuration. It might
look something like this:
1

// config/autoload/global.php

2
3
4
5
6
7
8
9
10
11
12
13

return array(
’db’ => array(
’driver’
=> ’Pdo’,
’dsn’
=> ’mysql:dbname=zf2tutorial;host=localhost’,
),
’service_manager’ => array(
’factories’ => array(
’ZendDbAdapterAdapter’ => ’ZendDbAdapterAdapterServiceFactory’,
),
),
);

The adapter can then be accessed in any ServiceLocatorAware classes.
1
2
3
4
5
6
7
8

public function getAdapter()
{
if (!$this->adapter) {
$sm = $this->getServiceLocator();
$this->adapter = $sm->get(’ZendDbAdapterAdapter’);
}
return $this->adapter;
}

105
Zend Framework 2 Documentation, Release 2.2.5

More information on adapter options can be found in the docs for ZendDbAdapter.

22.3 Setting a static adapter
In
order
to
utilize
this
adapter
in
non-ServiceLocatorAware
classes,
you
ZendDbTableGatewayFeatureGlobalAdapterFeature::setStaticAdapter()
static adapter:
1

can
use
to set a

// config/autoload/global.php

2
3
4
5
6
7
8
9
10
11
12

return array(
’db’ => array(
’driver’
=> ’Pdo’,
’dsn’
=> ’mysql:dbname=zf2tutorial;host=localhost’,
),
’service_manager’ => array(
’factories’ => array(
’ZendDbAdapterAdapter’ => function ($serviceManager) {
$adapterFactory = new ZendDbAdapterAdapterServiceFactory();
$adapter = $adapterFactory->createService($serviceManager);

13

ZendDbTableGatewayFeatureGlobalAdapterFeature::setStaticAdapter($adapter);

14
15

return $adapter;

16

}

17

),

18

),

19
20

);

The adapter can then later be fetched using ZendDbTableGatewayFeatureGlobalAdapterFeature::getStaticAda
for use in e.g. ZendValidatorDbRecordExists:
1
2
3
4
5
6
7

$validator = new ZendValidatorDbRecordExists(
array(
’table’
=> ’users’,
’field’
=> ’emailaddress’,
’adapter’ => ZendDbTableGatewayFeatureGlobalAdapterFeature::getStaticAdapter()
)
);

106

Chapter 22. Setting up a database adapter
CHAPTER 23

Migration from Zend Framework 1

This guide is intended to provide tools and strategies for migrating from Zend Framework 1 to Zend Framework 2.
There is no single solution that will work for every project, nor any tools to automate the process.
In this guide, we will cover the following:
• Tools for namespacing your code.
• Tools for consuming Zend Framework 2 within your Zend Framework 1 application.
• Strategies for running Zend Framework 2 and Zend Framework 1 in parallel.
• Strategies for making your code easier to migrate, focussing primarily on clean separation of your domain logic
and the MVC layer.
• Strategies for migrating the MVC layer.
• Strategies for migrating your domain layer.

107
Zend Framework 2 Documentation, Release 2.2.5

108

Chapter 23. Migration from Zend Framework 1
CHAPTER 24

Namespacing Old Classes

ZF2’s minimal version is PHP 5.3. The most notable feature of PHP 5.3 is the addition of namespaces, which ZF2 fully
embraces. Moreover, new projects built on ZF2 also fully embrace PHP namespaces. The addition of namespaces to
PHP has greatly improved the readability of long class names and has helped better organize code into modules and
components. This transition has also given birth to some naming best practices that help developers organize their
code bases consisting of classes, components, and modules in a consistent and clean fashion.
Converting an older code base that follows the original PEAR/ZF underscore separated class naming convention into
a properly namespaced codebase is one of the easier strategies to employ in both modernizing your code base as well
as getting ready to ZF2-ify your ZF1 application.
We’ve created a tool to help in this endeavor, it is located here:
https://github.com/zendframework/Namespacer
This tool will take a wholesale approach to converting older code like the following:
class My_Long_NestedComponent_ClassName
{
// methods that use other classes
}

into:
namespace MyLongNestedComponent;
use OtherClasses;
use SomethingElseConsumed;
class ClassName
{
// methods with classes converted to short name from use statement.
}

Some IDEs have this capability to some degree. That said, a good approach might be to use the command line
Namespacer to do a full sweep of your codebase, then use the IDE to make more specific naming changes that
might makes more sense to your application.

109
Zend Framework 2 Documentation, Release 2.2.5

24.1 Namespacing a ZF1 Application
The above Namespacer is a generalized tool. It does not understand the structure and naming conventions of a ZF1
application. As such, you’ll need to address the problem of converting your classes according to their role, and which
classes you find you can convert without affecting the way the framework interoperates with your code.
For example, in ZF1, the naming convention of application and module layer classes does not directly match up
with same well-defined library class/file conventions of the PEAR/ZF namings. For a standard ZF1 application, in
the application/ directory, controller classes are not prefixed, yet model and form classes are prefixed with
Application_. Moreover, they exist inside of lowercased directories, such as models or forms, and their file
to class name segment matching picks up only after the first segment. As an example, you might have this directory
structure with the class names on the right:
application/
-- Bootstrap.php
-- configs
| -- application.ini
| -- application.ini.dist
-- controllers
| -- IndexController.php
[class IndexController]
| -- PurchaseOrderController.php [class PurchaseOrderController]
-- forms
| -- PurchaseOrder
|
-- Payment.php
[class Application_Form_PurchaseOrder_Payment]
-- layouts
| -- scripts
|
-- main.phtml
|
-- subpage.phtml
-- models
| -- DbTable
| | -- Invoice.php
[Application_Model_DbTable_Invoice]
| -- Invoice.php
[Application_Model_Invoice]
| -- InvoiceRepository.php
[Application_Model_InvoiceRepository]
| -- Payment
| | -- Paypal
| |
-- DirectPayment.php [Application_Model_Payment_Paypal_DirectPayment]
| -- PurchaseOrder.php
[Application_Model_PurchaseOrder]
-- views
-- scripts
-- error
| -- error.phtml
-- index
| -- index.phtml
-- purchase-order
-- index.phtml
-- purchaser.phtml

It would not be a good strategy to attempt to do a wholesale namespacing of this kind of project for a number of
reasons:
1. ZF1 has special, context-aware autoloaders that will assist loading a class of a particular context from a special
location on disk. For example, ZF1 understands controllers will be located in the controllers directory and
will not be prefixed unless they are inside of a named module’s controllers directory.
2. Attempting to apply namespacing to controller classes would generally render a ZF1 application useless. ZF1,
beyond loading files from disk, assumes controllers will have a very specific naming convention so that they can
be invoked by the framework upon routing and dispatching.
3. Beyond dispatching, ZF1 uses the class name to identify and map the proper view script to automatically execute.
110

Chapter 24. Namespacing Old Classes
Zend Framework 2 Documentation, Release 2.2.5

By naming the controller something non-standard, views will no longer this this 1:1 mapping of controllers by
name to controller action named view scripts.
A better solution would be to start by namespacing the parts of your ZF1 application that have fewer tie-ins with the
ZF1 architecture. The place to start with this is models and forms.
Since models and forms do not touch controller and view classes (which make heavy use of ZF1 classes by way of
inheritance), model and form classes might not have the same level of coupling.

24.2 HOWTO Namespace Your Models
First, ensure your classes are under version control. The namespacer tool will make modification to classes in place.
You can then use your version control system as a diffing utility afterwards .
To run the tool, download the phar. Optionally you can place the namespacer.phar into a directory in your PATH.
Namespacing is a 2 part process:
1. Create a map of all the old files, new files, old classes and new classes.
2. Make the transformations according to the map file.
Change into your models/ directory and execute the map function:
namespacer.phar map --mapfile model-map.php --source models/

This will produce a file called model-map.php with entries like this:
1
2
3
4
5
6
7
8
9
10
11

<?php return array (
array (
’root_directory’ => ’/realpath/to/project/application/models’,
’original_class’ => ’Application_Model_Invoice’,
’original_file’ => ’/realpath/to/project/application/models/Invoice.php’,
’new_namespace’ => ’ApplicationModel’,
’new_class’ => ’Invoice’,
’new_file’ => ’/realpath/to/project/application/models/Application/Model/Invoice.php’,
),
...
);

This gives you an opportunity to manually edit the transformations if you so desire. While you can modify this file,
you also might find it to be easier to go with the default transformations, and do the remaining changes with your
IDE’s refactoring utility.
Once you are happy with the map file, run the transformations:
namespacer.phar transform --mapfile model-map.php

At this point, you can use your version control system’s status command to see how the directory has transformed.
As an example, in a sample project of mine, git reports the following:
renamed:
new file:
renamed:
renamed:
renamed:
renamed:
new file:
renamed:
renamed:

models/DbTable/Invoice.php -> models/Application/Model/DbTable/Invoice.php
models/Application/Model/DbTable/Transaction.php
models/Invoice.php -> models/Application/Model/Invoice.php
models/Payment/Paypal/DirectPayment.php -> models/Application/Model/Payment/Paypal/DirectPa
models/PurchaseOrder.php -> models/Application/Model/PurchaseOrder.php
models/PurchaseOrderRepository.php -> models/Application/Model/PurchaseOrderRepository.php
models/Application/Model/PurchaseOrderService.php
models/Purchaser.php -> models/Application/Model/Purchaser.php
models/Ticket.php -> models/Application/Model/Ticket.php

24.2. HOWTO Namespace Your Models

111
Zend Framework 2 Documentation, Release 2.2.5

renamed:
renamed:
deleted:
deleted:

models/Transaction.php -> models/Application/Model/Transaction.php
models/TransactionRepository.php -> models/Application/Model/TransactionRepository.php
models/DbTable/Transaction.php
models/PurchaseOrderService.php

You’ll notice that the resulting files have treated the models/ directory as the autoloader root directory. That means
that from this root, class files follow the strict PEAR/ZF2 classfile naming convention. The contents of one of the files
will look like this:
1

namespace ApplicationModel;

2
3
4
5

use ApplicationModelPurchaseOrder;
use ApplicationModelTransaction;
use Zend_Filter_Alnum;

6
7
8

class Invoice
{

9

protected $tickets;
protected $transaction;

10
11
12

...

13
14

}

Things to notice here:
• A namespace has been created for this class.
• The namespacer has created PHP use statements for classes known in the map file.
• Unknown classes are also included (for example, Zend classes) in use statements.
By keeping the old ZF1 classes, your models should continue to work if they consume ZF1 classes. This will allow
you to, at your own pace, transition your codebase to ZF2.
This same procedure can largely be adapted to forms and independent library code as well.

112

Chapter 24. Namespacing Old Classes
CHAPTER 25

Running Zend Framework 2 and Zend
Framework 1 in parallel

From a technical point of view it is absolutely possible to run ZF2 in parallel with ZF1 because there is no conflict
between the classnames due to the fact that ZF2 uses namespaces and ZF1 does not. Running ZF1 and ZF2 in
parallel can be used as a migration strategy in projects where it is not possible, or not convenient, to migrate an entire
application from ZF1 to ZF2. For instance, you could implement any new features of the application using ZF2, while
maintaining original ZF1 features.
Let’s examine some scenarios on how to execute ZF1 and ZF2 together.

25.1 Use ZF2 in a ZF1 project
Suppose we have an existing ZF1 application and we want to start using ZF2; how could we do that?
Because ZF2 uses namespaced classes, you can run it in parallel with ZF1 without naming conflicts. In order to do
this, you will need to add some code to autoload ZF2 from within your ZF1 project. Add these lines of code in your
public/index.php, before the instantiation of $application:
1
2
3
4
5
6

define(’ZF2_PATH’, ’/path/to/zf2/library’);
require_once ZF2_PATH . ’/Zend/Loader/StandardAutoloader.php’;
$loader = new ZendLoaderStandardAutoloader(array(
’autoregister_zf’ => true,
));
$loader->register();

We used the StandardAutoloader class from ZF2. Using this autoloader, classes with the initial namespace
Zend will be loaded using the ZF2_PATH, and any ZF1 classes will continue to be loaded via the mechanisms
present in ZF1.
Of course, this is not a real integration of ZF2 inside ZF1; it only provides the ability to consume ZF2 classes within
your ZF1 application. For instance, you cannot use the MVC architecture of ZF2 because you are using the MVC of
ZF1.
Evan Coury, a member of the ZF community review team, has produced a nice module for ZF1 (zf-2-for-1) that allows
you to use ZF2 features inside an existing ZF1 application. This module offers some basic integrations like the usage
of ZF2 view helpers in the ZF1 view layer (i.e. $this->zf2->get(’formRow’)).

113
Zend Framework 2 Documentation, Release 2.2.5

25.2 Use ZF1 in a ZF2 project
You can add ZF1 to your ZF2 application via Composer by adding the “zendframework/zendframework1” package as
a requirement.
For instance, if you have a ZF2 application and you want to install ZF 1.12, you need to add the following line in the
require section of your composer.json file:
"require": {
"php": ">=5.3.3",
"zendframework/zendframework1": "1.12",
...
}

After executing composer.phar update, you can start to use ZF1 classes in your ZF2 project. Since all ZF1
classes exist in the global namespace, you will need to refer to them by their full name; as examples, Zend_Date,
Zend_Feed_Reader, etc.
For other strategies on how to use ZF1 in a ZF2 project, you can check out this blog post by Abdul Malik Ikhsan, Zend
Framework 2 : Using Zend Framework 1 libraries.

25.3 Run ZF1 and ZF2 together
As we mentioned early, one way to migrate a ZF1 application to ZF2 can be to execute in parallel the different
versions of the framework, using ZF2 for the new features, and migrating the ZF1 code step by step. In order to
execute in parallel, we need to map different URLs to the different front controllers for ZF1 and ZF2. This goal can be
accomplished using the rewriting rules of your web server. From a performance point of view, this is the best solution
because it does not involve pre-processing overhead. For each URL we can define a different version of the framework
to be used.
For instance, imagine we have a ZF1 application and we want to use ZF2 only for URLs starting with /album. We
can use the following .htaccess file (this information is related to apache; if you are using another web server, read
the instructions in the note below):
1
2
3
4
5
6
7
8

SetEnv APPLICATION_ENV development
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^album(/.*)?$ index_zf2.php [NC,L]
RewriteRule ^.*$ index.php [NC,L]

index_zf2.php is a PHP script that includes as the typical public/index.php file of ZF2. Here is the source
code for index_zf2.php:
1

require_once ’../path-to-ZF2-app/public/index.php’;

We suggest putting the ZF2 application in a separate folder under the same root directory of the ZF1 application. In
this way you can continue to maintain the existing ZF1 code and use ZF2 only for the new features. Moreover, if you
want to migrate the old code you can do that by URL and switch to the new ZF2 code only when you are ready. This
approach can be useful to provide migration guideline without losing development time in a full stack migration.
Note: All web servers support a rewriting mechanism. For instance, if you are using Microsoft IIS 7, you can check
how to configure the rewriting rules from Rob Allen’s post Zend Framework URL Rewriting in IIS7; if you are using
nginx, you can check out this StackOverflow question: Zend Framework on nginx.
114

Chapter 25. Running Zend Framework 2 and Zend Framework 1 in parallel
Zend Framework 2 Documentation, Release 2.2.5

25.3. Run ZF1 and ZF2 together

115
Zend Framework 2 Documentation, Release 2.2.5

116

Chapter 25. Running Zend Framework 2 and Zend Framework 1 in parallel
CHAPTER 26

Introduction to ZendAuthentication

The ZendAuthentication component provides an API for authentication and includes concrete authentication
adapters for common use case scenarios.
ZendAuthentication is concerned only with authentication and not with authorization. Authentication is
loosely defined as determining whether an entity actually is what it purports to be (i.e., identification), based on some
set of credentials. Authorization, the process of deciding whether to allow an entity access to, or to perform operations
upon, other entities is outside the scope of ZendAuthentication. For more information about authorization and
access control with Zend Framework, please see the ZendPermissionsAcl or ZendPermissionsRbac component.
Note:
There is no ZendAuthenticationAuthentication class, instead the class
ZendAuthenticationAuthenticationService is provided. This class uses underlying authentication adapters and persistent storage backends.

26.1 Adapters
ZendAuthentication adapters are used to authenticate against a particular type of authentication service, such
as LDAP, RDBMS, or file-based storage. Different adapters are likely to have vastly different options and behaviors,
but some basic things are common among authentication adapters. For example, accepting authentication credentials (including a purported identity), performing queries against the authentication service, and returning results are
common to ZendAuthentication adapters.
Each ZendAuthentication adapter class implements ZendAuthenticationAdapterAdapterInterface.
This interface defines one method, authenticate(), that an adapter class must implement for performing an
authentication query. Each adapter class must be prepared prior to calling authenticate(). Such adapter
preparation includes setting up credentials (e.g., username and password) and defining values for adapter-specific
configuration options, such as database connection settings for a database table adapter.
The following is an example authentication adapter that requires a username and password to be set for authentication.
Other details, such as how the authentication service is queried, have been omitted for brevity:
1

use ZendAuthenticationAdapterAdapterInterface;

2
3
4
5
6

class MyAuthAdapter implements AdapterInterface
{
/**
* Sets username and password for authentication

117
Zend Framework 2 Documentation, Release 2.2.5

*
* @return void
*/
public function __construct($username, $password)
{
// ...
}

7
8
9
10
11
12
13
14

/**
* Performs an authentication attempt
*
* @return ZendAuthenticationResult
* @throws ZendAuthenticationAdapterExceptionExceptionInterface
If authentication cannot be performed
*
*/
public function authenticate()
{
// ...
}

15
16
17
18
19
20
21
22
23
24
25
26

}

As indicated in its docblock, authenticate() must return an instance of ZendAuthenticationResult
(or of a class derived from ZendAuthenticationResult).
If for some reason performing
an authentication query is impossible, authenticate() should throw an exception that derives from
ZendAuthenticationAdapterExceptionExceptionInterface.

26.2 Results
ZendAuthentication adapters return an instance of ZendAuthenticationResult with
authenticate() in order to represent the results of an authentication attempt. Adapters populate the
ZendAuthenticationResult object upon construction, so that the following four methods provide a basic
set of user-facing operations that are common to the results of ZendAuthentication adapters:
• isValid()- returns TRUE if and only if the result represents a successful authentication attempt
• getCode()- returns a ZendAuthenticationResult constant identifier for determining the type of
authentication failure or whether success has occurred. This may be used in situations where the developer
wishes to distinguish among several authentication result types. This allows developers to maintain detailed authentication result statistics, for example. Another use of this feature is to provide specific, customized messages
to users for usability reasons, though developers are encouraged to consider the risks of providing such detailed
reasons to users, instead of a general authentication failure message. For more information, see the notes below.
• getIdentity()- returns the identity of the authentication attempt
• getMessages()- returns an array of messages regarding a failed authentication attempt
A developer may wish to branch based on the type of authentication result in order to perform more specific operations. Some operations developers might find useful are locking accounts after too many unsuccessful password
attempts, flagging an IP address after too many nonexistent identities are attempted, and providing specific, customized
authentication result messages to the user. The following result codes are available:
1

use ZendAuthenticationResult;

2
3
4
5
6

Result::SUCCESS
Result::FAILURE
Result::FAILURE_IDENTITY_NOT_FOUND
Result::FAILURE_IDENTITY_AMBIGUOUS

118

Chapter 26. Introduction to ZendAuthentication
Zend Framework 2 Documentation, Release 2.2.5

7
8

Result::FAILURE_CREDENTIAL_INVALID
Result::FAILURE_UNCATEGORIZED

The following example illustrates how a developer may branch on the result code:
1
2

// inside of AuthController / loginAction
$result = $this->auth->authenticate($adapter);

3
4

switch ($result->getCode()) {

5

case Result::FAILURE_IDENTITY_NOT_FOUND:
/** do stuff for nonexistent identity **/
break;

6
7
8
9

case Result::FAILURE_CREDENTIAL_INVALID:
/** do stuff for invalid credential **/
break;

10
11
12
13

case Result::SUCCESS:
/** do stuff for successful authentication **/
break;

14
15
16
17

default:
/** do stuff for other failure **/
break;

18
19
20
21

}

26.3 Identity Persistence
Authenticating a request that includes authentication credentials is useful per se, but it is also important to support
maintaining the authenticated identity without having to present the authentication credentials with each request.
HTTP is a stateless protocol, however, and techniques such as cookies and sessions have been developed in order to
facilitate maintaining state across multiple requests in server-side web applications.

26.3.1 Default Persistence in the PHP Session
By default, ZendAuthentication provides persistent storage of the identity from a successful authentication attempt using the PHP session.
Upon a successful authentication attempt,
ZendAuthenticationAuthenticationService::authenticate()
stores
the identity from the authentication result into persistent storage.
Unless specified otherwise,
ZendAuthenticationAuthenticationService
uses
a
storage
class
named
ZendAuthenticationStorageSession, which, in turn, uses ZendSession. A custom class may instead
be used by providing an object that implements ZendAuthenticationStorageStorageInterface to
ZendAuthenticationAuthenticationService::setStorage().
Note: If automatic persistent storage of the identity is not appropriate for a particular use case, then developers
may forget using the ZendAuthenticationAuthenticationService class altogether, instead using an
adapter class directly.

26.3. Identity Persistence

119
Zend Framework 2 Documentation, Release 2.2.5

Modifying the Session Namespace

ZendAuthenticationStorageSession
uses
a
session
namespace
of
‘Zend_Auth‘.
This namespace may be overridden by passing a different value to the constructor of
ZendAuthenticationStorageSession, and this value is internally passed along to the
constructor of ZendSessionContainer.
This should occur before authentication is attempted, since
ZendAuthenticationAuthenticationService::authenticate() performs the automatic
storage of the identity.
1
2

use ZendAuthenticationAuthenticationService;
use ZendAuthenticationStorageSession as SessionStorage;

3
4

$auth = new AuthenticationService();

5
6
7

// Use ’someNamespace’ instead of ’Zend_Auth’
$auth->setStorage(new SessionStorage(’someNamespace’));

8
9
10
11

/**
* @todo Set up the auth adapter, $authAdapter
*/

12
13
14
15

// Authenticate, saving the result, and persisting the identity on
// success
$result = $auth->authenticate($authAdapter);

26.3.2 Chain Storage
A website may have multiple storage in place. The Chain Storage can be used to glue these together.
The Chain can for example be configured to first use a Session Storage and then use a OAuth as a secondary
Storage. One could configure this in the following way:
1
2
3

$storage = new Chain;
$storage->add(new Session);
$storage->add(new OAuth); // Note: imaginary storage, not part of ZF2

Now if the Chain Storage is accessed its underlying Storage will get accessed in the order in which they were added
to the chain. Thus first the Session Storage is used. Now either:
• The Session Storage is non-empty and the Chain will use its contents.
• The Sesssion Storage is empty. Next the OAuth Storage is accessed.
– If this one is also empty the Chain will act as empty.
– If this one is non-empty the Chain will use its contents. However it will also populate all Storage with
higher priority. Thus the Session Storage will be populated with the contents of the Oauth Storage.
The priority of Storage in the Chain can be made explicit via the Chain::add method.
1
2

$chain->add(new A, 2);
$chain->add(new B, 10); // First use B

26.3.3 Implementing Customized Storage
Sometimes developers may need to use a different identity storage mechanism than that provided
by ZendAuthenticationStorageSession.
For such cases developers may simply imple-

120

Chapter 26. Introduction to ZendAuthentication
Zend Framework 2 Documentation, Release 2.2.5

ment ZendAuthenticationStorageStorageInterface and supply an instance of the class to
ZendAuthenticationAuthenticationService::setStorage().
Using a Custom Storage Class

In order to use an identity persistence storage class other than ZendAuthenticationStorageSession, a
developer implements ZendAuthenticationStorageStorageInterface:
1

use ZendAuthenticationStorageStorageInterface;

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

class MyStorage implements StorageInterface
{
/**
* Returns true if and only if storage is empty
*
* @throws ZendAuthenticationExceptionExceptionInterface
If it is impossible to
*
determine whether storage is empty
*
* @return boolean
*/
public function isEmpty()
{
/**
* @todo implementation
*/
}

19
20
21
22
23
24
25
26
27
28

/**
* Returns the contents of storage
*
* Behavior is undefined when storage is empty.
*
* @throws ZendAuthenticationExceptionExceptionInterface
If reading contents from storage is impossible
*
* @return mixed
*/

29
30
31
32
33
34
35

public function read()
{
/**
* @todo implementation
*/
}

36
37
38
39
40
41
42
43
44

/**
* Writes $contents to storage
*
* @param mixed $contents
* @throws ZendAuthenticationExceptionExceptionInterface
If writing $contents to storage is impossible
*
* @return void
*/

45
46
47
48
49

public function write($contents)
{
/**
* @todo implementation

26.3. Identity Persistence

121
Zend Framework 2 Documentation, Release 2.2.5

*/

50

}

51
52

/**
* Clears contents from storage
*
* @throws ZendAuthenticationExceptionExceptionInterface
If clearing contents from storage is impossible
*
* @return void
*/

53
54
55
56
57
58
59
60

public function clear()
{
/**
* @todo implementation
*/
}

61
62
63
64
65
66
67

}

In order to use this custom storage class, ZendAuthenticationAuthenticationService::setStorage()
is invoked before an authentication query is attempted:
1

use ZendAuthenticationAuthenticationService;

2
3
4

// Instruct AuthenticationService to use the custom storage class
$auth = new AuthenticationService();

5
6

$auth->setStorage(new MyStorage());

7
8
9
10

/**
* @todo Set up the auth adapter, $authAdapter
*/

11
12
13
14

// Authenticate, saving the result, and persisting the identity on
// success
$result = $auth->authenticate($authAdapter);

26.4 Usage
There are two provided ways to use ZendAuthentication adapters:
• indirectly, through ZendAuthenticationAuthenticationService::authenticate()
• directly, through the adapter’s authenticate() method
The following example illustrates how to use a ZendAuthentication adapter indirectly, through the use of the
ZendAuthenticationAuthenticationService class:
1

use ZendAuthenticationAuthenticationService;

2
3
4

// instantiate the authentication service
$auth = new AuthenticationService();

5
6
7

// Set up the authentication adapter
$authAdapter = new MyAuthAdapter($username, $password);

8
9

// Attempt authentication, saving the result

122

Chapter 26. Introduction to ZendAuthentication
Zend Framework 2 Documentation, Release 2.2.5

10

$result = $auth->authenticate($authAdapter);

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

if (!$result->isValid()) {
// Authentication failed; print the reasons why
foreach ($result->getMessages() as $message) {
echo "$messagen";
}
} else {
// Authentication succeeded; the identity ($username) is stored
// in the session
// $result->getIdentity() === $auth->getIdentity()
// $result->getIdentity() === $username
}

Once authentication has been attempted in a request, as in the above example, it is a simple matter to check whether a
successfully authenticated identity exists:
1

use ZendAuthenticationAuthenticationService;

2
3

$auth = new AuthenticationService();

4
5
6
7

/**
* @todo Set up the auth adapter, $authAdapter
*/

8
9
10
11
12

if ($auth->hasIdentity()) {
// Identity exists; get it
$identity = $auth->getIdentity();
}

To remove an identity from persistent storage, simply use the clearIdentity() method. This typically would be
used for implementing an application “logout” operation:
1

$auth->clearIdentity();

When the automatic use of persistent storage is inappropriate for a particular use case, a developer may simply
bypass the use of the ZendAuthenticationAuthenticationService class, using an adapter class directly. Direct use of an adapter class involves configuring and preparing an adapter object and then calling its
authenticate() method. Adapter-specific details are discussed in the documentation for each adapter. The
following example directly utilizes MyAuthAdapter:
1
2

// Set up the authentication adapter
$authAdapter = new MyAuthAdapter($username, $password);

3
4
5

// Attempt authentication, saving the result
$result = $authAdapter->authenticate();

6
7
8
9
10
11
12
13
14
15

if (!$result->isValid()) {
// Authentication failed; print the reasons why
foreach ($result->getMessages() as $message) {
echo "$messagen";
}
} else {
// Authentication succeeded
// $result->getIdentity() === $username
}

26.4. Usage

123
Zend Framework 2 Documentation, Release 2.2.5

124

Chapter 26. Introduction to ZendAuthentication
CHAPTER 27

Database Table Authentication

27.1 Introduction
ZendAuthenticationAdapterDbTable provides the ability to authenticate against credentials stored
in a database table.
Because ZendAuthenticationAdapterDbTable requires an instance of
ZendDbAdapterAdapter to be passed to its constructor, each instance is bound to a particular database
connection. Other configuration options may be set through the constructor and through instance methods, one for
each option.
The available configuration options include:
• tableName: This is the name of the database table that contains the authentication credentials, and against
which the database authentication query is performed.
• identityColumn: This is the name of the database table column used to represent the identity. The identity
column must contain unique values, such as a username or e-mail address.
• credentialColumn: This is the name of the database table column used to represent the credential. Under a
simple identity and password authentication scheme, the credential value corresponds to the password. See also
the credentialTreatment option.
• credentialTreatment: In many cases, passwords and other sensitive data are encrypted, hashed, encoded, obscured, salted or otherwise treated through some function or algorithm. By specifying a parameterized treatment
string with this method, such as ‘MD5(?)‘ or ‘PASSWORD(?)‘, a developer may apply such arbitrary SQL upon
input credential data. Since these functions are specific to the underlying RDBMS, check the database manual
for the availability of such functions for your database system.

27.2 Basic Usage
As explained in the introduction, the ZendAuthenticationAdapterDbTable constructor requires an instance of ZendDbAdapterAdapter that serves as the database connection to which the authentication adapter
instance is bound. First, the database connection should be created.
The following code creates an adapter for an in-memory database, creates a simple table schema, and inserts a row
against which we can perform an authentication query later. This example requires the PDO SQLite extension to be
available:

125
Zend Framework 2 Documentation, Release 2.2.5

1

use ZendDbAdapterAdapter as DbAdapter;

2
3
4
5
6
7

// Create a SQLite database connection
$dbAdapter = new DbAdapter(array(
’driver’ => ’Pdo_Sqlite’,
’database’ => ’path/to/sqlite.db’
));

8
9
10
11
12
13
14

// Build a simple table creation query
$sqlCreate = ’CREATE TABLE [users] (’
. ’[id] INTEGER NOT NULL PRIMARY KEY, ’
. ’[username] VARCHAR(50) UNIQUE NOT NULL, ’
. ’[password] VARCHAR(32) NULL, ’
. ’[real_name] VARCHAR(150) NULL)’;

15
16
17

// Create the authentication credentials table
$dbAdapter->query($sqlCreate);

18
19
20
21

// Build a query to insert a row for which authentication may succeed
$sqlInsert = "INSERT INTO users (username, password, real_name) "
. "VALUES (’my_username’, ’my_password’, ’My Real Name’)";

22
23
24

// Insert the data
$dbAdapter->query($sqlInsert);

With
the
database
connection
and
table
data
available,
an
instance
of
ZendAuthenticationAdapterDbTable may be created. Configuration option values may be passed to
the constructor or deferred as parameters to setter methods after instantiation:
1

use ZendAuthenticationAdapterDbTable as AuthAdapter;

2
3
4
5
6
7
8

// Configure the instance with constructor parameters...
$authAdapter = new AuthAdapter($dbAdapter,
’users’,
’username’,
’password’
);

9
10
11

// ...or configure the instance with setter methods
$authAdapter = new AuthAdapter($dbAdapter);

12
13
14
15
16
17

$authAdapter
->setTableName(’users’)
->setIdentityColumn(’username’)
->setCredentialColumn(’password’)
;

At this point, the authentication adapter instance is ready to accept authentication queries. In order to formulate an
authentication query, the input credential values are passed to the adapter prior to calling the authenticate()
method:
1
2
3
4
5

// Set the input credential values (e.g., from a login form)
$authAdapter
->setIdentity(’my_username’)
->setCredential(’my_password’)
;

6
7

// Perform the authentication query, saving the result

126

Chapter 27. Database Table Authentication
Zend Framework 2 Documentation, Release 2.2.5

In addition to the availability of the getIdentity() method upon the authentication result object,
ZendAuthenticationAdapterDbTable also supports retrieving the table row upon authentication success:
1
2

// Print the identity
echo $result->getIdentity() . "nn";

3
4
5

// Print the result row
print_r($authAdapter->getResultRowObject());

6
7
8

/* Output:
my_username

9
10
11
12
13
14
15
16
17

Array
(
[id] => 1
[username] => my_username
[password] => my_password
[real_name] => My Real Name
)
*/

Since the table row contains the credential value, it is important to secure the values against unintended access.
When retrieving the result object, we can either specify what columns to return, or what columns to omit:
1
2
3
4

$columnsToReturn = array(
’id’, ’username’, ’real_name’
);
print_r($authAdapter->getResultRowObject($columnsToReturn));

5
6

/* Output:

7
8
9
10
11
12
13
14

Array
(
[id] => 1
[username] => my_username
[real_name] => My Real Name
)
*/

15
16
17

$columnsToOmit = array(’password’);
print_r($authAdapter->getResultRowObject(null, $columnsToOmit);

18
19

/* Output:

20
21
22
23
24
25
26
27

Array
(
[id] => 1
[username] => my_username
[real_name] => My Real Name
)
*/

27.2. Basic Usage

127
Zend Framework 2 Documentation, Release 2.2.5

27.3 Advanced Usage: Persisting a DbTable Result Object
By default, ZendAuthenticationAdapterDbTable returns the identity supplied back to the auth object
upon successful authentication. Another use case scenario, where developers want to store to the persistent storage
mechanism of ZendAuthentication an identity object containing other useful information, is solved by using
the getResultRowObject() method to return a stdClass object. The following code snippet illustrates its use:
1
2

// authenticate with ZendAuthenticationAdapterDbTable
$result = $this->_auth->authenticate($adapter);

3
4
5
6
7
8
9
10
11

if ($result->isValid()) {
// store the identity as an object where only the username and
// real_name have been returned
$storage = $this->_auth->getStorage();
$storage->write($adapter->getResultRowObject(array(
’username’,
’real_name’,
)));

12

// store the identity as an object where the password column has
// been omitted
$storage->write($adapter->getResultRowObject(
null,
’password’
));

13
14
15
16
17
18
19

/* ... */

20
21
22

} else {

23

/* ... */

24
25
26

}

27.3.1 Advanced Usage By Example
While the primary purpose of the ZendAuthentication component (and consequently
ZendAuthenticationAdapterDbTable) is primarily authentication and not authorization, there
are a few instances and problems that toe the line between which domain they fit within. Depending on how you’ve
decided to explain your problem, it sometimes makes sense to solve what could look like an authorization problem
within the authentication adapter.
With that disclaimer out of the way, ZendAuthenticationAdapterDbTable has some built in mechanisms that can be leveraged for additional checks at authentication time to solve some common user problems.
1

use ZendAuthenticationAdapterDbTable as AuthAdapter;

2
3
4
5
6
7
8
9

// The status field value of an account is not equal to "compromised"
$adapter = new AuthAdapter($db,
’users’,
’username’,
’password’,
’MD5(?) AND status != "compromised"’
);

10
11

// The active field value of an account is equal to "TRUE"

128

Chapter 27. Database Table Authentication
Zend Framework 2 Documentation, Release 2.2.5

12
13
14
15
16
17

$adapter = new AuthAdapter($db,
’users’,
’username’,
’password’,
’MD5(?) AND active = "TRUE"’
);

Another scenario can be the implementation of a salting mechanism. Salting is a term referring to a technique which
can highly improve your application’s security. It’s based on the idea that concatenating a random string to every
password makes it impossible to accomplish a successful brute force attack on the database using pre-computed hash
values from a dictionary.
Therefore, we need to modify our table to store our salt string:
1
2
3

$sqlAlter = "ALTER TABLE [users] "
. "ADD COLUMN [password_salt] "
. "AFTER [password]";

Here’s a simple way to generate a salt string for every user at registration:
1
2
3
4

$dynamicSalt = ’’;
for ($i = 0; $i < 50; $i++) {
$dynamicSalt .= chr(rand(33, 126));
}

And now let’s build the adapter:
1
2
3
4
5
6

$adapter = new AuthAdapter($db,
’users’,
’username’,
’password’,
"MD5(CONCAT(’staticSalt’, ?, password_salt))"
);

Note: You can improve security even more by using a static salt value hard coded into your application. In the case
that your database is compromised (e. g. by an SQL injection attack) but your web server is intact your data is still
unusable for the attacker.
Another alternative is to use the getDbSelect() method of the ZendAuthenticationAdapterDbTable
after the adapter has been constructed. This method will return the ZendDbSqlSelect object instance it will
use to complete the authenticate() routine. It is important to note that this method will always return the same
object regardless if authenticate() has been called or not. This object will not have any of the identity or
credential information in it as those values are placed into the select object at authenticate() time.
An example of a situation where one might want to use the getDbSelect() method would check the status of a
user, in other words to see if that user’s account is enabled.
1
2
3
4
5
6
7

// Continuing with the example from above
$adapter = new AuthAdapter($db,
’users’,
’username’,
’password’,
’MD5(?)’
);

8
9
10
11

// get select object (by reference)
$select = $adapter->getDbSelect();
$select->where(’active = "TRUE"’);

27.3. Advanced Usage: Persisting a DbTable Result Object

129
Zend Framework 2 Documentation, Release 2.2.5

12
13
14

// authenticate, this ensures that users.active = TRUE
$adapter->authenticate();

130

Chapter 27. Database Table Authentication
CHAPTER 28

Digest Authentication

28.1 Introduction
Digest authentication is a method of HTTP authentication that improves upon Basic authentication by providing a way
to authenticate without having to transmit the password in clear text across the network.
This adapter allows authentication against text files containing lines having the basic elements of Digest authentication:
• username, such as “joe.user“
• realm, such as “Administrative Area“
• MD5 hash of the username, realm, and password, separated by colons
The above elements are separated by colons, as in the following example (in which the password is “somePassword”):
1

someUser:Some Realm:fde17b91c3a510ecbaf7dbd37f59d4f8

28.2 Specifics
The digest authentication adapter, ZendAuthenticationAdapterDigest, requires several input parameters:
• filename - Filename against which authentication queries are performed
• realm - Digest authentication realm
• username - Digest authentication user
• password - Password for the user of the realm
These parameters must be set prior to calling authenticate().

28.3 Identity
The digest authentication adapter returns a ZendAuthenticationResult object, which has been populated
with the identity as an array having keys of realm and username. The respective array values associated with these
keys correspond to the values set before authenticate() is called.

131
Zend Framework 2 Documentation, Release 2.2.5

1

use ZendAuthenticationAdapterDigest as AuthAdapter;

2
3
4
5
6

$adapter = new AuthAdapter($filename,
$realm,
$username,
$password);

7
8

$result = $adapter->authenticate();

9
10

$identity = $result->getIdentity();

11
12

print_r($identity);

13
14
15
16
17
18
19
20

/*
Array
(
[realm] => Some Realm
[username] => someUser
)
*/

132

Chapter 28. Digest Authentication
CHAPTER 29

HTTP Authentication Adapter

29.1 Introduction
ZendAuthenticationAdapterHttp provides a mostly-compliant implementation of RFC-2617, Basic and
Digest HTTP Authentication. Digest authentication is a method of HTTP authentication that improves upon Basic
authentication by providing a way to authenticate without having to transmit the password in clear text across the
network.
Major Features:
• Supports both Basic and Digest authentication.
• Issues challenges in all supported schemes, so client can respond with any scheme it supports.
• Supports proxy authentication.
• Includes support for authenticating against text files and provides an interface for authenticating against other
sources, such as databases.
There are a few notable features of RFC-2617 that are not implemented yet:
• Nonce tracking, which would allow for “stale” support, and increased replay attack protection.
• Authentication with integrity checking, or “auth-int”.
• Authentication-Info HTTP header.

29.2 Design Overview
This adapter consists of two sub-components, the HTTP authentication class itself, and the so-called “Resolvers.”
The HTTP authentication class encapsulates the logic for carrying out both Basic and Digest authentication. It uses
a Resolver to look up a client’s identity in some data store (text file by default), and retrieve the credentials from the
data store. The “resolved” credentials are then compared to the values submitted by the client to determine whether
authentication is successful.

133
Zend Framework 2 Documentation, Release 2.2.5

29.3 Configuration Options
The ZendAuthenticationAdapterHttp class requires a configuration array passed to its constructor.
There are several configuration options available, and some are required:
Table 29.1: Configuration Options
Option
Required
Name
acYes
cept_schemes
realm
Yes
diYes, when
gest_domains
accept_schemes
contains digest
nonce_timeout when
Yes,
accept_schemes
contains digest
use_opaqueNo
algoNo
rithm
proxy_authNo

Description
Determines which authentication schemes the adapter will accept from the
client. Must be a space-separated list containing ‘basic’ and/or ‘digest’.
Sets the authentication realm; usernames should be unique within a given
realm.
Space-separated list of URIs for which the same authentication information
is valid. The URIs need not all point to the same server.
Sets the number of seconds for which the nonce is valid. See notes below.

Specifies whether to send the opaque value in the header. True by default.
Specified the algorithm. Defaults to MD5, the only supported option (for
now).
Disabled by default. Enable to perform Proxy authentication, instead of
normal origin server authentication.

Note: The current implementation of the nonce_timeout has some interesting side effects. This setting is supposed to determine the valid lifetime of a given nonce, or effectively how long a client’s authentication information
is accepted. Currently, if it’s set to 3600 (for example), it will cause the adapter to prompt the client for new credentials every hour, on the hour. This will be resolved in a future release, once nonce tracking and stale support are
implemented.

29.4 Resolvers
The resolver’s job is to take a username and realm, and return some kind of credential value. Basic authentication
expects to receive the Base64 encoded version of the user’s password. Digest authentication expects to receive a hash
of the user’s username, the realm, and their password (each separated by colons). Currently, the only supported hash
algorithm is MD5.
ZendAuthenticationAdapterHttp
relies
on
objects
implementing
ZendAuthenticationAdapterHttpResolverInterface. A text file resolver class is included with
this adapter, but any other kind of resolver can be created simply by implementing the resolver interface.

29.4.1 File Resolver
The file resolver is a very simple class. It has a single property specifying a filename, which can also be passed to the
constructor. Its resolve() method walks through the text file, searching for a line with a matching username and
realm. The text file format similar to Apache htpasswd files:
1

<username>:<realm>:<credentials>n

Each line consists of three fields - username, realm, and credentials - each separated by a colon. The credentials field
is opaque to the file resolver; it simply returns that value as-is to the caller. Therefore, this same file format serves both
134

Chapter 29. HTTP Authentication Adapter
Zend Framework 2 Documentation, Release 2.2.5

Basic and Digest authentication. In Basic authentication, the credentials field should be written in clear text. In Digest
authentication, it should be the MD5 hash described above.
There are two equally easy ways to create a File resolver:
1
2
3

use ZendAuthenticationAdapterHttpFileResolver;
$path
= ’files/passwd.txt’;
$resolver = new FileResolver($path);

or
1
2
3

$path
= ’files/passwd.txt’;
$resolver = new FileResolver();
$resolver->setFile($path);

If the given path is empty or not readable, an exception is thrown.

29.5 Basic Usage
First, set up an array with the required configuration values:
1
2
3
4
5
6

$config = array(
’accept_schemes’
’realm’
’digest_domains’
’nonce_timeout’
);

=>
=>
=>
=>

’basic digest’,
’My Web Site’,
’/members_only /my_account’,
3600,

This array will cause the adapter to accept either Basic or Digest authentication, and will require authenticated access
to all the areas of the site under /members_only and /my_account. The realm value is usually displayed by the
browser in the password dialog box. The nonce_timeout, of course, behaves as described above.
Next, create the ZendAuthenticationAdapterHttp object:
1

$adapter = new ZendAuthenticationAdapterHttp($config);

Since we’re supporting both Basic and Digest authentication, we need two different resolver objects. Note that this
could just as easily be two different classes:
1

use ZendAuthenticationAdapterHttpFileResolver;

2
3
4

$basicResolver = new FileResolver();
$basicResolver->setFile(’files/basicPasswd.txt’);

5
6
7

$digestResolver = new FileResolver();
$digestResolver->setFile(’files/digestPasswd.txt’);

8
9
10

$adapter->setBasicResolver($basicResolver);
$adapter->setDigestResolver($digestResolver);

Finally, we perform the authentication. The adapter needs a reference to both the Request and Response objects in
order to do its job:
1
2

assert($request instanceof ZendHttpRequest);
assert($response instanceof ZendHttpResponse);

3
4
5

$adapter->setRequest($request);
$adapter->setResponse($response);

29.5. Basic Usage

135
Zend Framework 2 Documentation, Release 2.2.5

6
7
8
9
10

$result = $adapter->authenticate();
if (!$result->isValid()) {
// Bad username/password, or canceled password prompt
}

136

Chapter 29. HTTP Authentication Adapter
CHAPTER 30

LDAP Authentication

30.1 Introduction
ZendAuthenticationAdapterLdap supports web application authentication with LDAP services. Its features include username and domain name canonicalization, multi-domain authentication, and failover capabilities. It
has been tested to work with Microsoft Active Directory and OpenLDAP, but it should also work with other LDAP
service providers.
This documentation includes a guide on using ZendAuthenticationAdapterLdap, an exploration of its
API, an outline of the various available options, diagnostic information for troubleshooting authentication problems,
and example options for both Active Directory and OpenLDAP servers.

30.2 Usage
To incorporate ZendAuthenticationAdapterLdap authentication into your application quickly, even if
you’re not using ZendMvc, the meat of your code should look something like the following:
1
2
3
4
5
6
7

use
use
use
use
use
use
use

ZendAuthenticationAuthenticationService;
ZendAuthenticationAdapterLdap as AuthAdapter;
ZendConfigReaderIni as ConfigReader;
ZendConfigConfig;
ZendLogLogger;
ZendLogWriterStream as LogWriter;
ZendLogFilterPriority as LogFilter;

8
9
10

$username = $this->getRequest()->getPost(’username’);
$password = $this->getRequest()->getPost(’password’);

11
12
13

$auth = new AuthenticationService();

14
15
16
17

$configReader = new ConfigReader();
$configData = $configReader->fromFile(’./ldap-config.ini’);
$config = new Config($configData, true);

18
19
20

$log_path = $config->production->ldap->log_path;
$options = $config->production->ldap->toArray();

137
Zend Framework 2 Documentation, Release 2.2.5

21

unset($options[’log_path’]);

22
23
24
25

$adapter = new AuthAdapter($options,
$username,
$password);

26
27

$result = $auth->authenticate($adapter);

28
29
30

if ($log_path) {
$messages = $result->getMessages();

31

$logger = new Logger;
$writer = new LogWriter($log_path);

32
33
34

$logger->addWriter($writer);

35
36

$filter = new LogFilter(Logger::DEBUG);
$writer->addFilter($filter);

37
38
39

foreach ($messages as $i => $message) {
if ($i-- > 1) { // $messages[2] and up are log messages
$message = str_replace("n", "n ", $message);
$logger->debug("Ldap: $i: $message");
}
}

40
41
42
43
44
45
46

}

Of course, the logging code is optional, but it is highly recommended that you use a logger.
ZendAuthenticationAdapterLdap will record just about every bit of information anyone could want
in $messages (more below), which is a nice feature in itself for something that has a history of being notoriously
difficult to debug.
The ZendConfigReaderIni code is used above to load the adapter options. It is also optional. A regular array
would work equally well. The following is an example ldap-config.ini file that has options for two separate
servers. With multiple sets of server options the adapter will try each, in order, until the credentials are successfully
authenticated. The names of the servers (e.g., ‘server1’ and ‘server2’) are largely arbitrary. For details regarding the
options array, see the Server Options section below. Note that ZendConfigReaderIni requires that any
values with “equals” characters (=) will need to be quoted (like the DNs shown below).
1

[production]

2
3

ldap.log_path = /tmp/ldap.log

4
5
6
7
8
9
10
11
12
13

; Typical options for OpenLDAP
ldap.server1.host = s0.foo.net
ldap.server1.accountDomainName = foo.net
ldap.server1.accountDomainNameShort = FOO
ldap.server1.accountCanonicalForm = 3
ldap.server1.username = "CN=user1,DC=foo,DC=net"
ldap.server1.password = pass1
ldap.server1.baseDn = "OU=Sales,DC=foo,DC=net"
ldap.server1.bindRequiresDn = true

14
15
16
17
18
19

; Typical options for Active Directory
ldap.server2.host = dc1.w.net
ldap.server2.useStartTls = true
ldap.server2.accountDomainName = w.net
ldap.server2.accountDomainNameShort = W

138

Chapter 30. LDAP Authentication
Zend Framework 2 Documentation, Release 2.2.5

20
21

ldap.server2.accountCanonicalForm = 3
ldap.server2.baseDn = "CN=Users,DC=w,DC=net"

The above configuration will instruct ZendAuthenticationAdapterLdap to attempt to authenticate users
with the OpenLDAP server s0.foo.net first. If the authentication fails for any reason, the AD server dc1.w.net
will be tried.
With servers in different domains, this configuration illustrates multi-domain authentication. You can also have multiple servers in the same domain to provide redundancy.
Note that in this case, even though OpenLDAP has no need for the short NetBIOS style domain name used by Windows, we provide it here for name canonicalization purposes (described in the Username Canonicalization section
below).

30.3 The API
The ZendAuthenticationAdapterLdap constructor accepts three parameters.
The $options parameter is required and must be an array containing one or more sets of options. Note that it is an
array of arrays of ZendLdapLdap options. Even if you will be using only one LDAP server, the options must still
be within another array.
Below is print_r() output of an example options parameter containing two sets of server options for LDAP servers
s0.foo.net and dc1.w.net (the same options as the above INI representation):
1
2
3
4
5
6
7
8
9
10
11

Array
(
[server2] => Array
(
[host] => dc1.w.net
[useStartTls] => 1
[accountDomainName] => w.net
[accountDomainNameShort] => W
[accountCanonicalForm] => 3
[baseDn] => CN=Users,DC=w,DC=net
)

12

[server1] => Array
(
[host] => s0.foo.net
[accountDomainName] => foo.net
[accountDomainNameShort] => FOO
[accountCanonicalForm] => 3
[username] => CN=user1,DC=foo,DC=net
[password] => pass1
[baseDn] => OU=Sales,DC=foo,DC=net
[bindRequiresDn] => 1
)

13
14
15
16
17
18
19
20
21
22
23
24
25

)

The information provided in each set of options above is different mainly because AD does not require a username be
in DN form when binding (see the bindRequiresDn option in the Server Options section below), which means
we can omit a number of options associated with retrieving the DN for a username being authenticated.
Note: What is a Distinguished Name?

30.3. The API

139
Zend Framework 2 Documentation, Release 2.2.5

A DN or “distinguished name” is a string that represents the path to an object within the LDAP directory. Each
comma-separated component is an attribute and value representing a node. The components are evaluated in reverse. For example, the user account CN=Bob Carter,CN=Users,DC=w,DC=net is located directly within the
CN=Users,DC=w,DC=net container. This structure is best explored with an LDAP browser like the ADSI Edit
MMC snap-in for Active Directory or phpLDAPadmin.
The names of servers (e.g. ‘server1’ and ‘server2’ shown above) are largely arbitrary, but for the sake of using
ZendConfigReaderIni, the identifiers should be present (as opposed to being numeric indexes) and should
not contain any special characters used by the associated file formats (e.g. the ‘.‘INI property separator, ‘&‘ for XML
entity references, etc).
With multiple sets of server options, the adapter can authenticate users in multiple domains and provide failover so
that if one server is not available, another will be queried.
Note: The Gory Details: What Happens in the Authenticate Method?
When the authenticate() method is called, the adapter iterates over each set of server options, sets them on
the internal ZendLdapLdap instance, and calls the ZendLdapLdap::bind() method with the username
and password being authenticated. The ZendLdapLdap class checks to see if the username is qualified with a
domain (e.g., has a domain component like alice@foo.net or FOOalice). If a domain is present, but does
not match either of the server’s domain names (foo.net or FOO), a special exception is thrown and caught by
ZendAuthenticationAdapterLdap that causes that server to be ignored and the next set of server options is selected. If a domain does match, or if the user did not supply a qualified username, ZendLdapLdap
proceeds to try to bind with the supplied credentials. if the bind is not successful, ZendLdapLdap throws a
ZendLdapExceptionLdapException which is caught by ZendAuthenticationAdapterLdap
and the next set of server options is tried. If the bind is successful, the iteration stops, and the adapter’s
authenticate() method returns a successful result. If all server options have been tried without success, the
authentication fails, and authenticate() returns a failure result with error messages from the last iteration.
The username and password parameters of the ZendAuthenticationAdapterLdap constructor represent
the credentials being authenticated (i.e., the credentials supplied by the user through your HTML login form). Alternatively, they may also be set with the setUsername() and setPassword() methods.

30.4 Server Options
Each set of server options in the context of ZendAuthenticationAdapterLdap consists of the following options,
which are passed, largely unmodified, to ZendLdapLdap::setOptions():

140

Chapter 30. LDAP Authentication
Zend Framework 2 Documentation, Release 2.2.5

Table 30.1: Server Options
Name
host
port

Description
The hostname of LDAP server that these options represent. This option is required.
The port on which the LDAP server is listening. If useSsl is TRUE, the default port value is
636. If useSsl is FALSE, the default port value is 389.
useStartTls
Whether or not the LDAP client should use TLS (aka SSLv2) encrypted transport. A value of
TRUE is strongly favored in production environments to prevent passwords from be transmitted
in clear text. The default value is FALSE, as servers frequently require that a certificate be
installed separately after installation. The useSsl and useStartTls options are mutually
exclusive. The useStartTls option should be favored over useSsl but not all servers support this
newer mechanism.
useSsl
Whether or not the LDAP client should use SSL encrypted transport. The useSsl and
useStartTls options are mutually exclusive, but useStartTls should be favored if the server and
LDAP client library support it. This value also changes the default port value (see port
description above).
username
The DN of the account used to perform account DN lookups. LDAP servers that require the
username to be in DN form when performing the “bind” require this option. Meaning, if
bindRequiresDn is TRUE, this option is required. This account does not need to be a privileged
account; an account with read-only access to objects under the baseDn is all that is necessary
(and preferred based on the Principle of Least Privilege).
password
The password of the account used to perform account DN lookups. If this option is not supplied,
the LDAP client will attempt an “anonymous bind” when performing account DN lookups.
bindRequiresDn Some LDAP servers require that the username used to bind be in DN form like CN=Alice
Baker,OU=Sales,DC=foo,DC=net (basically all servers except AD). If this option is TRUE, this
instructs ZendLdapLdap to automatically retrieve the DN corresponding to the username
being authenticated, if it is not already in DN form, and then re-bind with the proper DN. The
default value is FALSE. Currently only Microsoft Active Directory Server (ADS) is known not
to require usernames to be in DN form when binding, and therefore this option may be FALSE
with AD (and it should be, as retrieving the DN requires an extra round trip to the server).
Otherwise, this option must be set to TRUE (e.g. for OpenLDAP). This option also controls the
default accountFilterFormat used when searching for accounts. See the accountFilterFormat
option.
baseDn
The DN under which all accounts being authenticated are located. This option is required. if
you are uncertain about the correct baseDn value, it should be sufficient to derive it from the
user’s DNS domain using DC= components. For example, if the user’s principal name is
alice@foo.net, a baseDn of DC=foo,DC=net should work. A more precise location (e.g.,
OU=Sales,DC=foo,DC=net) will be more efficient, however.
accountCanon- A value of 2, 3 or 4 indicating the form to which account names should be canonicalized after
icalForm
successful authentication. Values are as follows: 2 for traditional username style names (e.g.,
alice), 3 for backslash-style names (e.g., FOOalice) or 4 for principal style usernames (e.g.,
alice@foo.net). The default value is 4 (e.g., alice@foo.net). For example, with a value of 3, the
identity returned by ZendAuthenticationResult::getIdentity() (and
ZendAuthenticationAuthenticationService::getIdentity(), if
ZendAuthenticationAuthenticationService was used) will always be FOOalice, regardless of
what form Alice supplied, whether it be alice, alice@foo.net, FOOalice, FoOaLicE,
foo.netalice, etc. See the Account Name Canonicalization section in the ZendLdapLdap
documentation for details. Note that when using multiple sets of server options it is
recommended, but not required, that the same accountCanonicalForm be used with all server
options so that the resulting usernames are always canonicalized to the same form (e.g., if you
canonicalize to EXAMPLEusername with an AD server but to username@example.com with
an OpenLDAP server, that may be awkward for the application’s high-level logic).
accountDoThe FQDN domain name for which the target LDAP server is an authority (e.g., example.com).
mainName
This option is used to canonicalize names so that the username supplied by the user can be
converted as necessary for binding. It is also used to determine if the server is an authority for
the supplied username (e.g., if accountDomainName is foo.net and the user supplies
30.4. Server Options
141
bob@bar.net, the server will not be queried, and a failure will result). This option is not
required, but if it is not supplied, usernames in principal name form (e.g., alice@foo.net) are
not supported. It is strongly recommended that you supply this option, as there are many
use-cases that require generating the principal name form.
Zend Framework 2 Documentation, Release 2.2.5

Note: If you enable useStartTls = TRUE or useSsl = TRUE you may find that the LDAP client generates an
error claiming that it cannot validate the server’s certificate. Assuming the PHP LDAP extension is ultimately linked
to the OpenLDAP client libraries, to resolve this issue you can set “TLS_REQCERT never” in the OpenLDAP
client ldap.conf (and restart the web server) to indicate to the OpenLDAP client library that you trust the server.
Alternatively, if you are concerned that the server could be spoofed, you can export the LDAP server’s root certificate
and put it on the web server so that the OpenLDAP client can validate the server’s identity.

30.5 Collecting Debugging Messages
ZendAuthenticationAdapterLdap collects debugging information within its authenticate()
method. This information is stored in the ZendAuthenticationResult object as messages. The array returned by ZendAuthenticationResult::getMessages() is described as follows
Table 30.2: Debugging Messages
Messages
Array Index
Index 0
Index 1
Indexes 2 and
higher

Description
A generic, user-friendly message that is suitable for displaying to users (e.g., “Invalid
credentials”). If the authentication is successful, this string is empty.
A more detailed error message that is not suitable to be displayed to users but should be logged
for the benefit of server operators. If the authentication is successful, this string is empty.
All log messages in order starting at index 2.

In practice, index 0 should be displayed to the user (e.g., using the FlashMessenger helper), index 1 should be logged
and, if debugging information is being collected, indexes 2 and higher could be logged as well (although the final
message always includes the string from index 1).

30.6 Common Options for Specific Servers
30.6.1 Options for Active Directory
For ADS, the following options are noteworthy:

142

Chapter 30. LDAP Authentication
Zend Framework 2 Documentation, Release 2.2.5

Table 30.3: Options for Active Directory
Name
host
useStartTls
useSsl
baseDn

accountCanonicalForm

accountDomainName
accountDomainNameShort

Additional Notes
As with all servers, this option is required.
For the sake of security, this should be TRUE if the server has the necessary certificate installed.
Possibly used as an alternative to useStartTls (see above).
As with all servers, this option is required. By default AD places all user accounts under the
Users container (e.g., CN=Users,DC=foo,DC=net), but the default is not common in larger
organizations. Ask your AD administrator what the best DN for accounts for your application
would be.
You almost certainly want this to be 3 for backslash style names (e.g., FOOalice), which are
most familiar to Windows users. You should not use the unqualified form 2 (e.g., alice), as this
may grant access to your application to users with the same username in other trusted domains
(e.g., BARalice and FOOalice will be treated as the same user). (See also note below.)
This is required with AD unless accountCanonicalForm 2 is used, which, again, is discouraged.
The NetBIOS name of the domain that users are in and for which the AD server is an authority.
This is required if the backslash style accountCanonicalForm is used.

Note:
Technically there should be no danger of accidental cross-domain authentication with the current
ZendAuthenticationAdapterLdap implementation, since server domains are explicitly checked, but this
may not be true of a future implementation that discovers the domain at runtime, or if an alternative adapter is used
(e.g., Kerberos). In general, account name ambiguity is known to be the source of security issues, so always try to use
qualified account names.

30.6.2 Options for OpenLDAP
For OpenLDAP or a generic LDAP server using a typical posixAccount style schema, the following options are noteworthy:

30.6. Common Options for Specific Servers

143
Zend Framework 2 Documentation, Release 2.2.5

Table 30.4: Options for OpenLDAP
Name
host
useStartTls
useSsl
username

Additional Notes
As with all servers, this option is required.
For the sake of security, this should be TRUE if the server has the necessary certificate installed.
Possibly used as an alternative to useStartTls (see above).
Required and must be a DN, as OpenLDAP requires that usernames be in DN form when
performing a bind. Try to use an unprivileged account.
password
The password corresponding to the username above, but this may be omitted if the LDAP
server permits an anonymous binding to query user accounts.
bindRequiresDn Required and must be TRUE, as OpenLDAP requires that usernames be in DN form when
performing a bind.
baseDn
As with all servers, this option is required and indicates the DN under which all accounts being
authenticated are located.
accountCanon- Optional, but the default value is 4 (principal style names like alice@foo.net), which may not
icalForm
be ideal if your users are used to backslash style names (e.g., FOOalice). For backslash style
names use value 3.
accountDoRequired unless you’re using accountCanonicalForm 2, which is not recommended.
mainName
accountDoIf AD is not also being used, this value is not required. Otherwise, if accountCanonicalForm 3
mainis used, this option is required and should be a short name that corresponds adequately to the
NameShort
accountDomainName (e.g., if your accountDomainName is foo.net, a good
accountDomainNameShort value might be FOO).

144

Chapter 30. LDAP Authentication
CHAPTER 31

Authentication Validator

31.1 Introduction
ZendAuthenticationValidatorAuthentication provides the ability to utilize a validator for an InputFilter in the instance of a Form or for single use where you simply want a true/false value and being able to
introspect the error.
The available configuration options include:
• adapter: This is an instance of ZendAuthenticationAdapter.
• identity: This is the identity or name of the identity in the passed in context.
• credential: This is the credential or the name of the credential in the passed in context.
• service: This is an instance of ZendAuthenticationAuthenticationService

31.2 Basic Usage
1
2

use ZendAuthenticationAuthenticationService;
use ZendAuthenticationValidatorAuthentication as AuthenticationValidator;

3
4
5
6
7
8
9

$service = new AuthenticationService();
$adapter = new MyAuthenticationAdapter();
$validator = new AuthenticationValidator(
’service’ => $service,
’adapter’ => $adapter,
);

10
11
12
13
14

$validator->setCredential(’myCredentialContext’);
$validator->isValid(’myIdentity’, array(
’myCredentialContext’ => ’myCredential’,
));

145
Zend Framework 2 Documentation, Release 2.2.5

146

Chapter 31. Authentication Validator
CHAPTER 32

Introduction to ZendBarcode

ZendBarcodeBarcode provides a generic way to generate barcodes. The ZendBarcode component is
divided into two subcomponents: barcode objects and renderers. Objects allow you to create barcodes independently
of the renderer. Renderer allow you to draw barcodes based on the support required.

147
Zend Framework 2 Documentation, Release 2.2.5

148

Chapter 32. Introduction to ZendBarcode
CHAPTER 33

Barcode creation using
ZendBarcodeBarcode class

33.1 Using ZendBarcodeBarcode::factory
ZendBarcodeBarcode uses a factory method to create an instance of a renderer that extends
ZendBarcodeRendererAbstractRenderer. The factory method accepts five arguments.
• The name of the barcode format (e.g., “code39”) or a Traversable object (required)
• The name of the renderer (e.g., “image”) (required)
• Options to pass to the barcode object (an array or a Traversable object) (optional)
• Options to pass to the renderer object (an array or a Traversable object) (optional)
• Boolean to indicate whether or not to automatically render errors. If an exception occurs, the provided barcode
object will be replaced with an Error representation (optional default TRUE)
Getting a Renderer with ZendBarcodeBarcode::factory()

ZendBarcodeBarcode::factory() instantiates barcode classes and renderers and ties them together. In
this first example, we will use the Code39 barcode type together with the Image renderer.
1

use ZendBarcodeBarcode;

2
3
4

// Only the text to draw is required
$barcodeOptions = array(’text’ => ’ZEND-FRAMEWORK’);

5
6
7
8
9
10

// No required options
$rendererOptions = array();
$renderer = Barcode::factory(
’code39’, ’image’, $barcodeOptions, $rendererOptions
);

149
Zend Framework 2 Documentation, Release 2.2.5

Using ZendBarcodeBarcode::factory() with ZendConfigConfig objects

You may pass a ZendConfigConfig object to the factory in order to create the necessary objects. The following
example is functionally equivalent to the previous.
1
2

use ZendConfigConfig;
use ZendBarcodeBarcode;

3
4
5
6
7
8
9
10

// Using only one ZendConfigConfig object
$config = new Config(array(
’barcode’
=> ’code39’,
’barcodeParams’ => array(’text’ => ’ZEND-FRAMEWORK’),
’renderer’
=> ’image’,
’rendererParams’ => array(’imageType’ => ’gif’),
));

11
12

$renderer = Barcode::factory($config);

33.2 Drawing a barcode
When you draw the barcode, you retrieve the resource in which the barcode is drawn. To draw a barcode, you can call
the draw() of the renderer, or simply use the proxy method provided by ZendBarcodeBarcode.
Drawing a barcode with the renderer object

1

use ZendBarcodeBarcode;

2
3
4

// Only the text to draw is required
$barcodeOptions = array(’text’ => ’ZEND-FRAMEWORK’);

5
6
7

// No required options
$rendererOptions = array();

8
9
10
11
12

// Draw the barcode in a new image,
$imageResource = Barcode::factory(
’code39’, ’image’, $barcodeOptions, $rendererOptions
)->draw();

Drawing a barcode with ZendBarcodeBarcode::draw()

1

use ZendBarcodeBarcode;

2
3
4

// Only the text to draw is required
$barcodeOptions = array(’text’ => ’ZEND-FRAMEWORK’);

5
6
7

// No required options
$rendererOptions = array();

8
9
10
11
12

// Draw the barcode in a new image,
$imageResource = Barcode::draw(
’code39’, ’image’, $barcodeOptions, $rendererOptions
);

150

Chapter 33. Barcode creation using ZendBarcodeBarcode class
Zend Framework 2 Documentation, Release 2.2.5

33.3 Rendering a barcode
When you render a barcode, you draw the barcode, you send the headers and you send the resource (e.g. to a browser).
To render a barcode, you can call the render() method of the renderer or simply use the proxy method provided by
ZendBarcodeBarcode.
Rendering a barcode with the renderer object

1

use ZendBarcodeBarcode;

2
3
4

// Only the text to draw is required
$barcodeOptions = array(’text’ => ’ZEND-FRAMEWORK’);

5
6
7

// No required options
$rendererOptions = array();

8
9
10
11
12
13

// Draw the barcode in a new image,
// send the headers and the image
Barcode::factory(
’code39’, ’image’, $barcodeOptions, $rendererOptions
)->render();

This will generate this barcode:

Rendering a barcode with ZendBarcodeBarcode::render()

1

use ZendBarcodeBarcode;

2
3
4

// Only the text to draw is required
$barcodeOptions = array(’text’ => ’ZEND-FRAMEWORK’);

5
6
7

// No required options
$rendererOptions = array();

8
9
10
11
12
13

// Draw the barcode in a new image,
// send the headers and the image
Barcode::render(
’code39’, ’image’, $barcodeOptions, $rendererOptions
);

This will generate the same barcode as the previous example.

33.3. Rendering a barcode

151
Zend Framework 2 Documentation, Release 2.2.5

152

Chapter 33. Barcode creation using ZendBarcodeBarcode class
CHAPTER 34

ZendBarcodeBarcode Objects

Barcode objects allow you to generate barcodes independently of the rendering support. After generation, you can
retrieve the barcode as an array of drawing instructions that you can provide to a renderer.
Objects have a large number of options. Most of them are common to all objects. These options can be set in three
ways:
• As an array or a Traversable object passed to the constructor.
• As an array passed to the setOptions() method.
• Via individual setters for each configuration type.
Different ways to parameterize a barcode object

1

use ZendBarcodeObject;

2
3

$options = array(’text’ => ’ZEND-FRAMEWORK’, ’barHeight’ => 40);

4
5
6

// Case 1: constructor
$barcode = new ObjectCode39($options);

7
8
9
10

// Case 2: setOptions()
$barcode = new ObjectCode39();
$barcode->setOptions($options);

11
12
13
14
15

// Case 3: individual setters
$barcode = new ObjectCode39();
$barcode->setText(’ZEND-FRAMEWORK’)
->setBarHeight(40);

34.1 Common Options
In the following list, the values have no units; we will use the term “unit.” For example, the default value of the
“thin bar” is “1 unit”. The real units depend on the rendering support (see the renderers documentation for more
information). Setters are each named by uppercasing the initial letter of the option and prefixing the name with
“set” (e.g. “barHeight” becomes “setBarHeight”). All options have a corresponding getter prefixed with “get” (e.g.
“getBarHeight”). Available options are:
153
Zend Framework 2 Documentation, Release 2.2.5

Table 34.1: Common Options
Option

Integer
Integer

Default
Description
Value
ZendBarcodeObject
Namespace of the barcode; for example, if you need to extend the
embedding objects
50
Height of the bars
3
Width of the thick bar

Integer

1

Width of the thin bar

Integer

1

foreColor

Integer

backgroundColor
orientation
font

Integer or
String
Float
String or
Integer
Float
Boolean
Boolean

0x000000
(black)
0xFFFFFF
(white)
0
NULL

Factor by which to multiply bar widths and font sizes (barHeight,
barThinWidth, barThickWidth and fontSize)
Color of the bar and the text. Could be provided as an integer or as a
HTML value (e.g. “#333333”)
Color of the background. Could be provided as an integer or as a
HTML value (e.g. “#333333”)
Orientation of the barcode
Font path to a TTF font or a number between 1 and 5 if using image
generation with GD (internal fonts)
Size of the font (not applicable with numeric fonts)
Draw a border around the barcode and the quiet zones
Leave a quiet zone before and after the barcode

barcodeNamespace
barHeight
barThickWidth
barThinWidth
factor

fontSize
withBorder
withQuietZones
drawText
stretchText
withChecksum
withChecksumInText
text

Data
Type
String

10
FALSE
TRUE

Boolean
Boolean
Boolean

TRUE
FALSE
FALSE

Boolean

FALSE

String

NULL

Set if the text is displayed below the barcode
Specify if the text is stretched all along the barcode
Indicate whether or not the checksum is automatically added to the
barcode
Indicate whether or not the checksum is displayed in the textual
representation
The text to represent as a barcode

34.1.1 Particular case of static setBarcodeFont()
You can set a common font for all your objects by using the static method
ZendBarcodeBarcode::setBarcodeFont(). This value can be always be overridden for individual objects by using the setFont() method.
1

use ZendBarcodeBarcode;

2
3
4

// In your bootstrap:
Barcode::setBarcodeFont(’my_font.ttf’);

5
6
7
8
9
10
11

// Later in your code:
Barcode::render(
’code39’,
’pdf’,
array(’text’ => ’ZEND-FRAMEWORK’)
); // will use ’my_font.ttf’

12
13
14
15
16

// or:
Barcode::render(
’code39’,
’image’,

154

Chapter 34. ZendBarcodeBarcode Objects
Zend Framework 2 Documentation, Release 2.2.5

17
18
19
20
21

array(
’text’ => ’ZEND-FRAMEWORK’,
’font’ => 3
)
); // will use the 3rd GD internal font

34.2 Common Additional Getters
Table 34.2: Common Getters
Getter
getType()

Data
Type
String

Description

getRawText()
getTextToDisplay()
getQuietZone()

String
String
Integer

getInstructions()
getHeight($recalculate =
false)
getWidth($recalculate
= false)
getOffsetTop($recalculate =
false)
getOffsetLeft($recalculate =
false)

Array
Integer

Return the name of the barcode class without the namespace (e.g.
ZendBarcodeObjectCode39 returns simply “code39”)
Return the original text provided to the object
Return the text to display, including, if activated, the checksum value
Return the size of the space needed before and after the barcode without any
drawing
Return drawing instructions as an array.
Return the height of the barcode calculated after possible rotation

Integer

Return the width of the barcode calculated after possible rotation

Integer

Return the position of the top of the barcode calculated after possible rotation

Integer

Return the position of the left of the barcode calculated after possible rotation

34.3 Description of shipped barcodes
You will find below detailed information about all barcode types shipped by default with Zend Framework.

34.3.1 ZendBarcodeObjectError

This barcode is a special case. It is internally used to automatically render an exception caught by the ZendBarcode
component.

34.2. Common Additional Getters

155
Zend Framework 2 Documentation, Release 2.2.5

34.3.2 ZendBarcodeObjectCode128

• Name: Code 128
• Allowed characters: the complete ASCII-character set
• Checksum: optional (modulo 103)
• Length: variable
There are no particular options for this barcode.

34.3.3 ZendBarcodeObjectCodabar

• Name: Codabar (or Code 2 of 7)
• Allowed characters:‘0123456789-$:/.+’ with ‘ABCD’ as start and stop characters
• Checksum: none
• Length: variable
There are no particular options for this barcode.

34.3.4 ZendBarcodeObjectCode25

• Name: Code 25 (or Code 2 of 5 or Code 25 Industrial)
• Allowed characters:‘0123456789’
• Checksum: optional (modulo 10)
• Length: variable
There are no particular options for this barcode.

156

Chapter 34. ZendBarcodeBarcode Objects
Zend Framework 2 Documentation, Release 2.2.5

34.3.5 ZendBarcodeObjectCode25interleaved

This barcode extends ZendBarcodeObjectCode25 (Code 2 of 5), and has the same particulars and options,
and adds the following:
• Name: Code 2 of 5 Interleaved
• Allowed characters:‘0123456789’
• Checksum: optional (modulo 10)
• Length: variable (always even number of characters)
Available options include:
Table 34.3: ZendBarcodeObjectCode25interleaved Options
Option
withBearerBars

Data Type
Boolean

Default Value
FALSE

Description
Draw a thick bar at the top and the bottom of the barcode.

Note: If the number of characters is not even, ZendBarcodeObjectCode25interleaved will automatically prepend the missing zero to the barcode text.

34.3.6 ZendBarcodeObjectEan2

This barcode extends ZendBarcodeObjectEan5 (EAN 5), and has the same particulars and options, and adds
the following:
• Name: EAN-2
• Allowed characters:‘0123456789’
• Checksum: only use internally but not displayed
• Length: 2 characters
There are no particular options for this barcode.
Note: If the number of characters is lower than 2, ZendBarcodeObjectEan2 will automatically prepend the
missing zero to the barcode text.

34.3. Description of shipped barcodes

157
Zend Framework 2 Documentation, Release 2.2.5

34.3.7 ZendBarcodeObjectEan5

This barcode extends ZendBarcodeObjectEan13 (EAN 13), and has the same particulars and options, and
adds the following:
• Name: EAN-5
• Allowed characters:‘0123456789’
• Checksum: only use internally but not displayed
• Length: 5 characters
There are no particular options for this barcode.
Note: If the number of characters is lower than 5, ZendBarcodeObjectEan5 will automatically prepend the
missing zero to the barcode text.

34.3.8 ZendBarcodeObjectEan8

This barcode extends ZendBarcodeObjectEan13 (EAN 13), and has the same particulars and options, and
adds the following:
• Name: EAN-8
• Allowed characters:‘0123456789’
• Checksum: mandatory (modulo 10)
• Length: 8 characters (including checksum)
There are no particular options for this barcode.
Note: If the number of characters is lower than 8, ZendBarcodeObjectEan8 will automatically prepend the
missing zero to the barcode text.

158

Chapter 34. ZendBarcodeBarcode Objects
Zend Framework 2 Documentation, Release 2.2.5

34.3.9 ZendBarcodeObjectEan13

• Name: EAN-13
• Allowed characters:‘0123456789’
• Checksum: mandatory (modulo 10)
• Length: 13 characters (including checksum)
There are no particular options for this barcode.
Note: If the number of characters is lower than 13, ZendBarcodeObjectEan13 will automatically prepend
the missing zero to the barcode text.
The option withQuietZones has no effect with this barcode.

34.3.10 ZendBarcodeObjectCode39

• Name: Code 39
• Allowed characters:‘0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ -.$/+%’
• Checksum: optional (modulo 43)
• Length: variable
Note: ZendBarcodeObjectCode39 will automatically add the start and stop characters (‘*’) for you.
There are no particular options for this barcode.

34.3.11 ZendBarcodeObjectIdentcode

This barcode extends ZendBarcodeObjectCode25interleaved (Code 2 of 5 Interleaved), and inherits
some of its capabilities; it also has a few particulars of its own.
• Name: Identcode (Deutsche Post Identcode)
34.3. Description of shipped barcodes

159
Zend Framework 2 Documentation, Release 2.2.5

• Allowed characters:‘0123456789’
• Checksum: mandatory (modulo 10 different from Code25)
• Length: 12 characters (including checksum)
There are no particular options for this barcode.
Note: If the number of characters is lower than 12, ZendBarcodeObjectIdentcode will automatically
prepend missing zeros to the barcode text.

34.3.12 ZendBarcodeObjectItf14

This barcode extends ZendBarcodeObjectCode25interleaved (Code 2 of 5 Interleaved), and inherits
some of its capabilities; it also has a few particulars of its own.
• Name: ITF-14
• Allowed characters:‘0123456789’
• Checksum: mandatory (modulo 10)
• Length: 14 characters (including checksum)
There are no particular options for this barcode.
Note: If the number of characters is lower than 14, ZendBarcodeObjectItf14 will automatically prepend
missing zeros to the barcode text.

34.3.13 ZendBarcodeObjectLeitcode

This barcode extends ZendBarcodeObjectIdentcode (Deutsche Post Identcode), and inherits some of its
capabilities; it also has a few particulars of its own.
• Name: Leitcode (Deutsche Post Leitcode)
• Allowed characters:‘0123456789’
• Checksum: mandatory (modulo 10 different from Code25)
• Length: 14 characters (including checksum)

160

Chapter 34. ZendBarcodeBarcode Objects
Zend Framework 2 Documentation, Release 2.2.5

There are no particular options for this barcode.
Note: If the number of characters is lower than 14, ZendBarcodeObjectLeitcode will automatically
prepend missing zeros to the barcode text.

34.3.14 ZendBarcodeObjectPlanet

• Name: Planet (PostaL Alpha Numeric Encoding Technique)
• Allowed characters:‘0123456789’
• Checksum: mandatory (modulo 10)
• Length: 12 or 14 characters (including checksum)
There are no particular options for this barcode.

34.3.15 ZendBarcodeObjectPostnet

• Name: Postnet (POSTal Numeric Encoding Technique)
• Allowed characters:‘0123456789’
• Checksum: mandatory (modulo 10)
• Length: 6, 7, 10 or 12 characters (including checksum)
There are no particular options for this barcode.

34.3.16 ZendBarcodeObjectRoyalmail

• Name: Royal Mail or RM4SCC (Royal Mail 4-State Customer Code)
• Allowed characters:‘0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ’
• Checksum: mandatory
• Length: variable
There are no particular options for this barcode.

34.3. Description of shipped barcodes

161
Zend Framework 2 Documentation, Release 2.2.5

34.3.17 ZendBarcodeObjectUpca

This barcode extends ZendBarcodeObjectEan13 (EAN-13), and inherits some of its capabilities; it also has
a few particulars of its own.
• Name: UPC-A (Universal Product Code)
• Allowed characters:‘0123456789’
• Checksum: mandatory (modulo 10)
• Length: 12 characters (including checksum)
There are no particular options for this barcode.
Note: If the number of characters is lower than 12, ZendBarcodeObjectUpca will automatically prepend
missing zeros to the barcode text.
The option withQuietZones has no effect with this barcode.

34.3.18 ZendBarcodeObjectUpce

This barcode extends ZendBarcodeObjectUpca (UPC-A), and inherits some of its capabilities; it also has a
few particulars of its own. The first character of the text to encode is the system (0 or 1).
• Name: UPC-E (Universal Product Code)
• Allowed characters:‘0123456789’
• Checksum: mandatory (modulo 10)
• Length: 8 characters (including checksum)
There are no particular options for this barcode.
Note: If the number of characters is lower than 8, ZendBarcodeObjectUpce will automatically prepend
missing zeros to the barcode text.

Note: If the first character of the text to encode is not 0 or 1, ZendBarcodeObjectUpce will automatically
replace it by 0.
The option withQuietZones has no effect with this barcode.

162

Chapter 34. ZendBarcodeBarcode Objects
CHAPTER 35

ZendBarcode Renderers

Renderers have some common options. These options can be set in three ways:
• As an array or a Traversable object passed to the constructor.
• As an array passed to the setOptions() method.
• As discrete values passed to individual setters.
Different ways to parameterize a renderer object

1

use ZendBarcodeRenderer;

2
3

$options = array(’topOffset’ => 10);

4
5
6

// Case 1
$renderer = new RendererPdf($options);

7
8
9
10

// Case 2
$renderer = new RendererPdf();
$renderer->setOptions($options);

11
12
13
14

// Case 3
$renderer = new RendererPdf();
$renderer->setTopOffset(10);

35.1 Common Options
In the following list, the values have no unit; we will use the term “unit.” For example, the default value of the “thin
bar” is “1 unit.” The real units depend on the rendering support. The individual setters are obtained by uppercasing the
initial letter of the option and prefixing the name with “set” (e.g. “barHeight” => “setBarHeight”). All options have a
correspondent getter prefixed with “get” (e.g. “getBarHeight”). Available options are:

163
Zend Framework 2 Documentation, Release 2.2.5

Table 35.1: Common Options
Option

Data
Type
String

Default
Description
Value
ZendBarcodeRenderer of the renderer; for example, if you need to extend the
Namespace
renderers

String

“left”

Can be “left”, “center” or “right”. Can be useful with PDF or if the
setWidth() method is used with an image renderer.

String

“top”

Integer

0

topOffset

Integer

0

automaticRenderError
moduleSize
barcode

Boolean

FALSE

Float

1

Can be “top”, “middle” or “bottom”. Can be useful with PDF or if the
setHeight() method is used with an image renderer.
Top position of the barcode inside the renderer. If used, this value will
override the “horizontalPosition” option.
Top position of the barcode inside the renderer. If used, this value will
override the “verticalPosition” option.
Whether or not to automatically render errors. If an exception occurs, the
provided barcode object will be replaced with an Error representation.
Note that some errors (or exceptions) can not be rendered.
Size of a rendering module in the support.

rendererNamespace
horizontalPosition
verticalPosition
leftOffset

ZendBarcodeObject
NULL

The barcode object to render.

An additional getter exists: getType(). It returns the name of the renderer class without the namespace (e.g.
ZendBarcodeRendererImage returns “image”).

35.2 ZendBarcodeRendererImage
The Image renderer will draw the instruction list of the barcode object in an image resource. The component requires
the GD extension. The default width of a module is 1 pixel.
Available options are:
Table 35.2: ZendBarcodeRendererImage Options
Option
height

Data
Type
Integer

Default
Value
0

width

Integer

0

imageType

String

“png”

Description
Allow you to specify the height of the result image. If “0”, the height will be
calculated by the barcode object.
Allow you to specify the width of the result image. If “0”, the width will be
calculated by the barcode object.
Specify the image format. Can be “png”, “jpeg”, “jpg” or “gif”.

35.3 ZendBarcodeRendererPdf
The PDF renderer will draw the instruction list of the barcode object in a PDF document. The default width of a
module is 0.5 point.
There are no particular options for this renderer.

164

Chapter 35. ZendBarcode Renderers
CHAPTER 36

ZendCacheStorageAdapter

36.1 Overview
Storage adapters are wrappers for real storage resources such as memory and the filesystem, using the
well known adapter pattern.
They come with tons of methods to read, write and modify stored items and to get information about
stored items and the storage.
All adapters implement the interface ZendCacheStorageStorageInterface and most extend ZendCacheStorageAdapterAbstractAdapter, which comes with basic logic.
Configuration is handled by either ZendCacheStorageAdapterAdapterOptions, or an
adapter-specific options class if it exists. You may pass the options instance to the class at instantiation
or via the setOptions() method, or alternately pass an associative array of options in either place
(internally, these are then passed to an options class instance). Alternately, you can pass either the options
instance or associative array to the ZendCacheStorageFactory::factory method.
Note: Many methods throw exceptions
Because many caching operations throw an exception on error, you need to catch them manually or you can use the
plug-in ZendCacheStoragePluginExceptionHandler with throw_exceptions set to false to
automatically catch them. You can also define an exception_callback to log exceptions.

36.2 Quick Start
Caching adapters can either be created from the provided ZendCacheStorageFactory factory,
or by simply instantiating one of the ZendCacheStorageAdapter* classes.
To make life easier, the ZendCacheStorageFactory comes with a factory method to create
an adapter and create/add all requested plugins at once.
1

use ZendCacheStorageFactory;

2
3
4
5

// Via factory:
$cache = StorageFactory::factory(array(
’adapter’ => array(

165
Zend Framework 2 Documentation, Release 2.2.5

’name’
=> ’apc’,
’options’ => array(’ttl’ => 3600),

6
7

),
’plugins’ => array(
’exception_handler’ => array(’throw_exceptions’ => false),
),

8
9
10
11
12

));

13
14
15
16
17
18
19

// Alternately:
$cache = StorageFactory::adapterFactory(’apc’, array(’ttl’ => 3600));
$plugin = StorageFactory::pluginFactory(’exception_handler’, array(
’throw_exceptions’ => false,
));
$cache->addPlugin($plugin);

20
21
22
23

// Or manually:
$cache = new ZendCacheStorageAdapterApc();
$cache->getOptions()->setTtl(3600);

24
25
26
27

$plugin = new ZendCacheStoragePluginExceptionHandler();
$plugin->getOptions()->setThrowExceptions(false);
$cache->addPlugin($plugin);

36.3 Basic Configuration Options
Basic configuration is handled by either ZendCacheStorageAdapterAdapterOptions, or
an adapter-specific options class if it exists. You may pass the options instance to the class at instantiation
or via the setOptions() method, or alternately pass an associative array of options in either place
(internally, these are then passed to an options class instance). Alternately, you can pass either the options
instance or associative array to the ZendCacheStorageFactory::factory method.
The following configuration options are defined by ZendCacheStorageAdapterAdapterOptions
and are available for every supported adapter. Adapter-specific configuration options are described on
adapter level below.
Option
ttl
namespace
key_pattern
readable
writable

Data Type
integer
string
null‘‘|‘‘string
boolean
boolean

Default Value
0
“zfcache”
null
true
true

Description
Time to live
The “namespace” in which cache items will live
Pattern against which to validate cache keys
Enable/Disable reading data from cache
Enable/Disable writing data to cache

36.4 The StorageInterface
The ZendCacheStorageStorageInterface is the basic interface implemented by all storage adapters.
getItem(string $key, boolean & $success = null, mixed & $casToken = null)
Load an item with the given $key.
If item exists set parameter $success to true, set parameter $casToken and returns mixed value of item.
If item can’t load set parameter $success to false and returns null.
Return type mixed

166

Chapter 36. ZendCacheStorageAdapter
Zend Framework 2 Documentation, Release 2.2.5

getItems(array $keys)
Load all items given by $keys returning key-value pairs.
Return type array
hasItem(string $key)
Test if an item exists.
Return type boolean
hasItems(array $keys)
Test multiple items.
Return type string[]
getMetadata(string $key)
Get metadata of an item.
Return type array|boolean
getMetadatas(array $keys)
Get multiple metadata.
Return type array
setItem(string $key, mixed $value)
Store an item.
Return type boolean
setItems(array $keyValuePairs)
Store multiple items.
Return type boolean
addItem(string $key, mixed $value)
Add an item.
Return type boolean
addItems(array $keyValuePairs)
Add multiple items.
Return type boolean
replaceItem(string $key, mixed $value)
Replace an item.
Return type boolean
replaceItems(array $keyValuePairs)
Replace multiple items.
Return type boolean
checkAndSetItem(mixed $token, string $key, mixed $value)
Set item only if token matches. It uses the token received from getItem() to check if the item has changed
before overwriting it.
Return type boolean
touchItem(string $key)
Reset lifetime of an item.
Return type boolean

36.4. The StorageInterface

167
Zend Framework 2 Documentation, Release 2.2.5

touchItems(array $keys)
Reset lifetime of multiple items.
Return type boolean
removeItem(string $key)
Remove an item.
Return type boolean
removeItems(array $keys)
Remove multiple items.
Return type boolean
incrementItem(string $key, int $value)
Increment an item.
Return type integer|boolean
incrementItems(array $keyValuePairs)
Increment multiple items.
Return type boolean
decrementItem(string $key, int $value)
Decrement an item.
Return type integer|boolean
decrementItems(array $keyValuePairs)
Decrement multiple items.
Return type boolean
getCapabilities()
Capabilities of this storage.
Return type ZendCacheStorageCapabilities

36.5 The AvailableSpaceCapableInterface
The ZendCacheStorageAvailableSpaceCapableInterface implements a method to make it possible getting the current available space of the storage.
getAvailableSpace()
Get available space in bytes.
Return type integer|float

36.6 The TotalSpaceCapableInterface
The ZendCacheStorageTotalSpaceCapableInterface implements a method to make it possible getting the total space of the storage.
getTotalSpace()
Get total space in bytes.
Return type integer|float

168

Chapter 36. ZendCacheStorageAdapter
Zend Framework 2 Documentation, Release 2.2.5

36.7 The ClearByNamespaceInterface
The ZendCacheStorageClearByNamespaceInterface implements a method to clear all items of a
given namespace.
clearByNamespace(string $namespace)
Remove items of given namespace.
Return type boolean

36.8 The ClearByPrefixInterface
The ZendCacheStorageClearByPrefixInterface implements a method to clear all items of a given
prefix (within the current configured namespace).
clearByPrefix(string $prefix)
Remove items matching given prefix.
Return type boolean

36.9 The ClearExpiredInterface
The ZendCacheStorageClearExpiredInterface implements a method to clear all expired items
(within the current configured namespace).
clearExpired()
Remove expired items.
Return type boolean

36.10 The FlushableInterface
The ZendCacheStorageFlushableInterface implements a method to flush the complete storage.
flush()
Flush the whole storage.
Return type boolean

36.11 The IterableInterface
The ZendCacheStorageIterableInterface implements a method to get an iterator to iterate over items
of the storage. It extends IteratorAggregate so it’s possible to directly iterate over the storage using foreach.
getIterator()
Get an Iterator.
Return type ZendCacheStorageIteratorInterface

36.7. The ClearByNamespaceInterface

169
Zend Framework 2 Documentation, Release 2.2.5

36.12 The OptimizableInterface
The ZendCacheStorageOptimizableInterface implements a method to run optimization processes on
the storage.
optimize()
Optimize the storage.
Return type boolean

36.13 The TaggableInterface
The ZendCacheStorageTaggableInterface implements methods to mark items with one or more tags
and to clean items matching tags.
setTags(string $key, string[] $tags)
Set tags to an item by given key. (An empty array will remove all tags)
Return type boolean
getTags(string $key)
Get tags of an item by given key.
Return type string[]|false
clearByTags(string[] $tags, boolean $disjunction = false)
Remove items matching given tags.
If $disjunction is true only one of the given tags must match else all given tags must match.
Return type boolean

36.14 The Apc Adapter
The ZendCacheStorageAdapterApc adapter stores cache items in shared memory through
the required PHP extension APC (Alternative PHP Cache).
This adapter implements the following interfaces:
• ZendCacheStorageStorageInterface
• ZendCacheStorageAvailableSpaceCapableInterface
• ZendCacheStorageClearByNamespaceInterface
• ZendCacheStorageClearByPrefixInterface
• ZendCacheStorageFlushableInterface
• ZendCacheStorageIterableInterface
• ZendCacheStorageTotalSpaceCapableInterface

170

Chapter 36. ZendCacheStorageAdapter
Zend Framework 2 Documentation, Release 2.2.5

Table 36.1: Capabilities
Capability
supportedDatatypes
supportedMetadata
minTtl
maxTtl
staticTtl
ttlPrecision
useRequestTime
expiredRead
maxKeyLength
namespaceIsPrefix
namespaceSeparator

Value
null, boolean, integer, double, string, array (serialized), object (serialized)
internal_key, atime, ctime, mtime, rtime, size, hits, ttl
1
0
true
1
<ini value of apc.use_request_time>
false
5182
true
<Option value of namespace_separator>

Table 36.2: Adapter specific options
Name
namespace_separator

Data Type
string

Default Value
”:”

Description
A separator for the namespace and prefix

36.15 The Dba Adapter
The ZendCacheStorageAdapterDba adapter stores cache items into dbm like databases using the required PHP extension dba.
This adapter implements the following interfaces:
• ZendCacheStorageStorageInterface
• ZendCacheStorageAvailableSpaceCapableInterface
• ZendCacheStorageClearByNamespaceInterface
• ZendCacheStorageClearByPrefixInterface
• ZendCacheStorageFlushableInterface
• ZendCacheStorageIterableInterface
• ZendCacheStorageOptimizableInterface
• ZendCacheStorageTotalSpaceCapableInterface
Table 36.3: Capabilities
Capability
supportedDatatypes
supportedMetadata
minTtl
maxKeyLength
namespaceIsPrefix
namespaceSeparator

Value
string, null => string, boolean => string, integer => string, double =>
string
<none>
0
0
true
<Option value of namespace_separator>

36.15. The Dba Adapter

171
Zend Framework 2 Documentation, Release 2.2.5

Table 36.4: Adapter specific options
Name

handler

Default
Value
”:”

string
string

“”
“c”

string

namespace_separator
pathname
mode

Data
Type
string

“flatfile”

Description
A separator for the namespace and prefix
Pathname to the database file
The mode to open the database Please read dba_open for more
information
The name of the handler which shall be used for accessing the
database.

Note: This adapter doesn’t support automatically expire items
Because of this adapter doesn’t support automatically expire items it’s very important to clean outdated items by self.

36.16 The Filesystem Adapter
The ZendCacheStorageAdapterFilesystem adapter stores cache items into the filesystem.
This adapter implements the following interfaces:
• ZendCacheStorageStorageInterface
• ZendCacheStorageAvailableSpaceCapableInterface
• ZendCacheStorageClearByNamespaceInterface
• ZendCacheStorageClearByPrefixInterface
• ZendCacheStorageClearExpiredInterface
• ZendCacheStorageFlushableInterface
• ZendCacheStorageIterableInterface
• ZendCacheStorageOptimizableInterface
• ZendCacheStorageTaggableInterface
• ZendCacheStorageTotalSpaceCapableInterface

172

Chapter 36. ZendCacheStorageAdapter
Zend Framework 2 Documentation, Release 2.2.5

Table 36.5: Capabilities
Capability
supportedDatatypes
supportedMetadata
minTtl
maxTtl
staticTtl
ttlPrecision
useRequestTime
expiredRead
maxKeyLength
namespaceIsPrefix
namespaceSeparator

Value
string, null => string, boolean => string, integer => string, double =>
string
mtime, filespec, atime, ctime
1
0
false
1
false
true
251
true
<Option value of namespace_separator>

Table 36.6: Adapter specific options
Name
namespace_separator
cache_dir
clear_stat_cache
dir_level

Data Type
string

Default Value
”:”

Description
A separator for the namespace and prefix

string
boolean
integer

“”
true
1

dir_permission

integer
false
boolean
integer
false
string
boolean
boolean
integer
false

0700

Directory to store cache files
Call clearstatcache() enabled?
Defines how much sub-directories should be
created
Set explicit permission on creating new
directories
Lock files on writing
Set explicit permission on creating new files

file_locking
file_permission
key_pattern
no_atime
no_ctime
umask

true
0600

/^[a-z0-9_+-]*$/Di
Validate key against pattern
true
Don’t get ‘fileatime’ as ‘atime’ on metadata
true
Don’t get ‘filectime’ as ‘ctime’ on metadata
false
Use umask to set file and directory
permissions

36.17 The Memcached Adapter
The ZendCacheStorageAdapterMemcached adapter stores cache items over the memcached protocol. It’s using the required PHP extension memcached which is based on Libmemcached.
This adapter implements the following interfaces:
• ZendCacheStorageStorageInterface
• ZendCacheStorageAvailableSpaceCapableInterface
• ZendCacheStorageFlushableInterface
• ZendCacheStorageTotalSpaceCapableInterface

36.17. The Memcached Adapter

173
Zend Framework 2 Documentation, Release 2.2.5

Table 36.7: Capabilities
Capability
supportedDatatypes
supportedMetadata
minTtl
maxTtl
staticTtl
ttlPrecision
useRequestTime
expiredRead
maxKeyLength
namespaceIsPrefix
namespaceSeparator

Value
null, boolean, integer, double, string, array (serialized), object (serialized)
<none>
1
0
true
1
false
false
255
true
<none>

Table 36.8: Adapter specific options
Name

Data
Type

Default
Value
servers array []
lib_options
array []

Description

List of servers in [] = array(string host, integer port)
Associative array of Libmemcached options were the array key is the option name
(without the prefix “OPT_”) or the constant value. The array value is the option value
Please read this<http://php.net/manual/memcached.setoption.php> for more
information

36.18 The Memory Adapter
The ZendCacheStorageAdapterMemory adapter stores cache items into the PHP process
using an array.
This adapter implements the following interfaces:
• ZendCacheStorageStorageInterface
• ZendCacheStorageAvailableSpaceCapableInterface
• ZendCacheStorageClearByPrefixInterface
• ZendCacheStorageClearExpiredInterface
• ZendCacheStorageFlushableInterface
• ZendCacheStorageIterableInterface
• ZendCacheStorageTaggableInterface
• ZendCacheStorageTotalSpaceCapableInterface

174

Chapter 36. ZendCacheStorageAdapter
Zend Framework 2 Documentation, Release 2.2.5

Table 36.9: Capabilities
Capability
supportedDatatypes
supportedMetadata
minTtl
maxTtl
staticTtl
ttlPrecision
useRequestTime
expiredRead
maxKeyLength
namespaceIsPrefix

Value
string, null, boolean, integer, double, array, object, resource
mtime
1
<Value of PHP_INT_MAX>
false
0.05
false
true
0
false

Table 36.10: Adapter specific options
Name
memory_limit

Data Type
string integer

Default Value
<50%
of
ini
memory_limit>

value

Description
Limit of how much memory
can PHP allocate to allow
store items into this adapter
• If the used memory of PHP exceeds
this limit an OutOfSpaceException will
be thrown.
• A number less or
equal 0 will disable
the memory limit
• When a number is
used, the value is
measured in bytes
(Shorthand notation
may also be used)

Note: All stored items will be lost after terminating the script.

36.19 The WinCache Adapter
The ZendCacheStorageAdapterWinCache adapter stores cache items into shared memory
through the required PHP extension WinCache.
This adapter implements the following interfaces:
• ZendCacheStorageStorageInterface
• ZendCacheStorageAvailableSpaceCapableInterface
• ZendCacheStorageFlushableInterface
• ZendCacheStorageTotalSpaceCapableInterface

36.19. The WinCache Adapter

175
Zend Framework 2 Documentation, Release 2.2.5

Table 36.11: Capabilities
Capability
supportedDatatypes
supportedMetadata
minTtl
maxTtl
staticTtl
ttlPrecision
useRequestTime
expiredRead
namespaceIsPrefix
namespaceSeparator

Value
null, boolean, integer, double, string, array (serialized), object (serialized)
internal_key, ttl, hits, size
1
0
true
1
<ini value of apc.use_request_time>
false
true
<Option value of namespace_separator>

Table 36.12: Adapter specific options
Name
namespace_separator

Data Type
string

Default Value
”:”

Description
A separator for the namespace and prefix

36.20 The XCache Adapter
The ZendCacheStorageAdapterXCache adapter stores cache items into shared memory
through the required PHP extension XCache.
This adapter implements the following interfaces:
• ZendCacheStorageStorageInterface
• ZendCacheStorageAvailableSpaceCapableInterface
• ZendCacheStorageClearByNamespaceInterface
• ZendCacheStorageClearByPrefixInterface
• ZendCacheStorageFlushableInterface
• ZendCacheStorageIterableInterface
• ZendCacheStorageTotalSpaceCapableInterface
Table 36.13: Capabilities
Capability
supportedDatatypes
supportedMetadata
minTtl
maxTtl
staticTtl
ttlPrecision
useRequestTime
expiredRead
maxKeyLength
namespaceIsPrefix
namespaceSeparator

176

Value
boolean, integer, double, string, array (serialized), object (serialized)
internal_key, size, refcount, hits, ctime, atime, hvalue
1
<ini value of xcache.var_maxttl>
true
1
true
false
5182
true
<Option value of namespace_separator>

Chapter 36. ZendCacheStorageAdapter
Zend Framework 2 Documentation, Release 2.2.5

Table 36.14: Adapter specific options
Name

Data
Type

Default
Value
namesstring”:”
pace_separator
adbooleanalse
f
min_auth

admin_user
admin_pass

Description

A separator for the namespace and prefix

string“”

Enable admin authentication by configuration options admin_user and
admin_pass
This makes XCache administration functions accessible if
xcache.admin.enable_auth is enabled without the need of
HTTP-Authentication.
The username of xcache.admin.user

string“”

The password of xcache.admin.pass in plain text

36.21 The ZendServerDisk Adapter
This ZendCacheStorageAdapterZendServerDisk adapter stores cache items on filesystem through the Zend Server Data Caching API.
This adapter implements the following interfaces:
• ZendCacheStorageStorageInterface
• ZendCacheStorageAvailableSpaceCapableInterface
• ZendCacheStorageClearByNamespaceInterface
• ZendCacheStorageFlushableInterface
• ZendCacheStorageTotalSpaceCapableInterface
Table 36.15: Capabilities
Capability
supportedDatatypes
supportedMetadata
minTtl
maxTtl
maxKeyLength
staticTtl
ttlPrecision
useRequestTime
expiredRead
namespaceIsPrefix
namespaceSeparator

Value
null, boolean, integer, double, string, array (serialized), object (serialized)
<none>
1
0
0
true
1
false
false
true
::

36.22 The ZendServerShm Adapter
The ZendCacheStorageAdapterZendServerShm adapter stores cache items in shared
memory through the Zend Server Data Caching API.

36.21. The ZendServerDisk Adapter

177
Zend Framework 2 Documentation, Release 2.2.5

This adapter implements the following interfaces:
• ZendCacheStorageStorageInterface
• ZendCacheStorageClearByNamespaceInterface
• ZendCacheStorageFlushableInterface
• ZendCacheStorageTotalSpaceCapableInterface
Table 36.16: Capabilities
Capability
supportedDatatypes
supportedMetadata
minTtl
maxTtl
maxKeyLength
staticTtl
ttlPrecision
useRequestTime
expiredRead
namespaceIsPrefix
namespaceSeparator

Value
null, boolean, integer, double, string, array (serialized), object (serialized)
<none>
1
0
0
true
1
false
false
true
::

36.23 Examples
Basic usage

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

$cache
= ZendCacheStorageFactory::factory(array(
’adapter’ => array(
’name’ => ’filesystem’
),
’plugins’ => array(
// Don’t throw exceptions on cache errors
’exception_handler’ => array(
’throw_exceptions’ => false
),
)
));
$key
= ’unique-cache-key’;
$result = $cache->getItem($key, $success);
if (!$success) {
$result = doExpensiveStuff();
$cache->setItem($key, $result);
}

Get multiple rows from db

1
2
3
4
5

// Instantiate the cache instance using a namespace for the same type of items
$cache
= ZendCacheStorageFactory::factory(array(
’adapter’ => array(
’name’
=> ’filesystem’
// With a namespace we can indicate the same type of items

178

Chapter 36. ZendCacheStorageAdapter
Zend Framework 2 Documentation, Release 2.2.5

// -> So we can simple use the db id as cache key
’options’ => array(
’namespace’ => ’dbtable’
),

6
7
8
9

),
’plugins’ => array(
// Don’t throw exceptions on cache errors
’exception_handler’ => array(
’throw_exceptions’ => false
),
// We store database rows on filesystem so we need to serialize them
’Serializer’
)

10
11
12
13
14
15
16
17
18
19

));

20
21
22
23
24
25
26
27
28
29
30
31

// Load two rows from cache if possible
$ids
= array(1, 2);
$results = $cache->getItems($ids);
if (count($results) < count($ids)) {
// Load rows from db if loading from cache failed
$missingIds
= array_diff($ids, array_keys($results));
$missingResults = array();
$query
= ’SELECT * FROM dbtable WHERE id IN (’ . implode(’,’, $missingIds) . ’)’;
foreach ($pdo->query($query, PDO::FETCH_ASSOC) as $row) {
$missingResults[ $row[’id’] ] = $row;
}

32

// Update cache items of the loaded rows from db
$cache->setItems($missingResults);

33
34
35

// merge results from cache and db
$results = array_merge($results, $missingResults);

36
37
38

}

36.23. Examples

179
Zend Framework 2 Documentation, Release 2.2.5

180

Chapter 36. ZendCacheStorageAdapter
CHAPTER 37

ZendCacheStorageCapabilities

37.1 Overview
Storage capabilities describes how a storage adapter works and which features it supports.
To get capabilities of a storage adapter, you can use the method getCapabilities() of the storage adapter but
only the storage adapter and its plugins have permissions to change them.
Because capabilities are mutable, for example, by changing some options, you can subscribe to the “change” event to
get notifications; see the examples for details.
If you are writing your own plugin or adapter, you can also change capabilities because you have access to the marker
object and can create your own marker to instantiate a new object of ZendCacheStorageCapabilities.

37.2 Available Methods
__construct(ZendCacheStorageStorageInterface $storage, stdClass $marker, array $capabilities = array(), ZendCacheStorageCapabilities|null $baseCapabilities = null)
Constructor
getSupportedDatatypes()
Get supported datatypes.
Return type array
setSupportedDatatypes(stdClass $marker, array $datatypes)
Set supported datatypes.
Return type ZendCacheStorageCapabilities
getSupportedMetadata()
Get supported metadata.
Return type array
setSupportedMetadata(stdClass $marker, string $metadata)
Set supported metadata.
Return type ZendCacheStorageCapabilities

181
Zend Framework 2 Documentation, Release 2.2.5

getMinTtl()
Get minimum supported time-to-live.
(Returning 0 means items never expire)
Return type integer
setMinTtl(stdClass $marker, int $minTtl)
Set minimum supported time-to-live.
Return type ZendCacheStorageCapabilities
getMaxTtl()
Get maximum supported time-to-live.
Return type integer
setMaxTtl(stdClass $marker, int $maxTtl)
Set maximum supported time-to-live.
Return type ZendCacheStorageCapabilities
getStaticTtl()
Is the time-to-live handled static (on write), or dynamic (on read).
Return type boolean
setStaticTtl(stdClass $marker, boolean $flag)
Set if the time-to-live is handled statically (on write) or dynamically (on read).
Return type ZendCacheStorageCapabilities
getTtlPrecision()
Get time-to-live precision.
Return type float
setTtlPrecision(stdClass $marker, float $ttlPrecision)
Set time-to-live precision.
Return type ZendCacheStorageCapabilities
getUseRequestTime()
Get the “use request time” flag status.
Return type boolean
setUseRequestTime(stdClass $marker, boolean $flag)
Set the “use request time” flag.
Return type ZendCacheStorageCapabilities
getExpiredRead()
Get flag indicating if expired items are readable.
Return type boolean
setExpiredRead(stdClass $marker, boolean $flag)
Set if expired items are readable.
Return type ZendCacheStorageCapabilities
getMaxKeyLength()
Get maximum key length.
Return type integer

182

Chapter 37. ZendCacheStorageCapabilities
Zend Framework 2 Documentation, Release 2.2.5

setMaxKeyLength(stdClass $marker, int $maxKeyLength)
Set maximum key length.
Return type ZendCacheStorageCapabilities
getNamespaceIsPrefix()
Get if namespace support is implemented as a key prefix.
Return type boolean
setNamespaceIsPrefix(stdClass $marker, boolean $flag)
Set if namespace support is implemented as a key prefix.
Return type ZendCacheStorageCapabilities
getNamespaceSeparator()
Get namespace separator if namespace is implemented as a key prefix.
Return type string
setNamespaceSeparator(stdClass $marker, string $separator)
Set the namespace separator if namespace is implemented as a key prefix.
Return type ZendCacheStorageCapabilities

37.3 Examples
Get storage capabilities and do specific stuff in base of it

1

use ZendCacheStorageFactory;

2
3
4

$cache = StorageFactory::adapterFactory(’filesystem’);
$supportedDatatypes = $cache->getCapabilities()->getSupportedDatatypes();

5
6
7
8
9
10
11

// now you can run specific stuff in base of supported feature
if ($supportedDatatypes[’object’]) {
$cache->set($key, $object);
} else {
$cache->set($key, serialize($object));
}

Listen to change event

1

use ZendCacheStorageFactory;

2
3
4
5

$cache = StorageFactory::adapterFactory(’filesystem’, array(
’no_atime’ => false,
));

6
7
8
9
10

// Catching capability changes
$cache->getEventManager()->attach(’capability’, function($event) {
echo count($event->getParams()) . ’ capabilities changed’;
});

11
12
13

// change option which changes capabilities
$cache->getOptions()->setNoATime(true);

37.3. Examples

183
Zend Framework 2 Documentation, Release 2.2.5

184

Chapter 37. ZendCacheStorageCapabilities
CHAPTER 38

ZendCacheStoragePlugin

38.1 Overview
Cache storage plugins are objects to add missing functionality or to influence behavior of a storage adapter.
The plugins listen to events the adapter triggers and can change called method arguments (*.post
- events), skipping and directly return a result (using stopPropagation), changing the result (with setResult of ZendCacheStoragePostEvent) and catching exceptions (with
ZendCacheStorageExceptionEvent).

38.2 Quick Start
Storage plugins can either be created from ZendCacheStorageFactory with the pluginFactory, or by
simply instantiating one of the ZendCacheStoragePlugin*classes.
To make life easier, the ZendCacheStorageFactory comes with the method factory to create an adapter
and all given plugins at once.
1

use ZendCacheStorageFactory;

2
3
4
5
6
7

// Via factory:
$cache = StorageFactory::factory(array(
’adapter’ => ’filesystem’,
’plugins’ => array(’serializer’),
));

8
9
10
11
12

// Alternately:
$cache = StorageFactory::adapterFactory(’filesystem’);
$plugin = StorageFactory::pluginFactory(’serializer’);
$cache->addPlugin($plugin);

13
14
15
16
17

// Or manually:
$cache = new ZendCacheStorageAdapterFilesystem();
$plugin = new ZendCacheStoragePluginSerializer();
$cache->addPlugin($plugin);

185
Zend Framework 2 Documentation, Release 2.2.5

38.3 The ClearExpiredByFactor Plugin
The ZendCacheStorageAdapterClearExpiredByFactor plugin calls the storage
method clearExpired() randomly (by factor) after every call of setItem(), setItems(),
addItem() and addItems().
Table 38.1: Plugin specific options
Name
clearing_factor

Data Type
integer

Default Value
0

Description
The automatic clearing factor

Note: ** The ClearExpiredInterface is required **
The storage have to implement the ZendCacheStorageClearExpiredInterface to work with this plugin.

38.4 The ExceptionHandler Plugin
The ZendCacheStorageAdapterExceptionHandler plugin catches all exceptions
thrown on reading or writing to cache and sends the exception to a defined callback function.
It’s configurable if the plugin should re-throw the catched exception.
Table 38.2: Plugin specific options
Name

Data Type

Default
Value
null

excepcallable
tion_callback
null
throw_exceptions boolean

Description
Callback will be called on an exception and get the
exception as argument
Re-throw catched exceptions

true

38.5 The IgnoreUserAbort Plugin
The ZendCacheStorageAdapterIgnoreUserAbort plugin ignores script terminations by
users until write operations to cache finished.
Table 38.3: Plugin specific options
Name
exit_on_abort

Data Type
boolean

Default Value
true

Description
Terminate script execution if user abort the script

38.6 The OptimizeByFactor Plugin
The ZendCacheStorageAdapterOptimizeByFactor plugin calls the storage method
optimize() randomly (by factor) after removing items from cache.
Table 38.4: Plugin specific options
Name
optimizing_factor

186

Data Type
integer

Default Value
0

Description
The automatic optimization factor

Chapter 38. ZendCacheStoragePlugin
Zend Framework 2 Documentation, Release 2.2.5

Note: ** The OptimizableInterface is required **
The storage have to implement the ZendCacheStorageOptimizableInterface to work with this plugin.

38.7 The Serializer Plugin
The ZendCacheStorageAdapterSerializer plugin will serialize data on writing to cache
and unserialize on reading. So it’s possible to store different datatypes into cache storages only support
strings.
Table 38.5: Plugin specific options
Name
serializer

Data Type
Default Value
Description
null
string null
The serializer to use
ZendSerializerAdapterAdapterInterface
• If null use the default serializer
• If string instantiate the serializer with
serializer_options

serializer_options

array

[]

Array of serializer options
used to instantiate the serializer

38.8 Available Methods
setOptions(ZendCacheStoragePluginPluginOptions $options)
Set options.
Return type ZendCacheStoragePluginPluginInterface
getOptions()
Get options.
Return type ZendCacheStoragePluginPluginOptions
attach(ZendEventManagerEventManagerInterface $events)
Defined by ZendEventManagerListenerAggregateInterface, attach one or more listeners.
Return type void
detach(ZendEventManagerEventManagerInterface $events)
Defined by ZendEventManagerListenerAggregateInterface, detach all previously attached
listeners.
Return type void

38.7. The Serializer Plugin

187
Zend Framework 2 Documentation, Release 2.2.5

38.9 Examples
Basics of writing an own storage plugin

1
2
3

use ZendCacheStorageEvent;
use ZendCacheStoragePluginAbstractPlugin;
use ZendEventManagerEventManagerInterface;

4
5
6

class MyPlugin extends AbstractPlugin
{

7

protected $handles = array();

8
9

// This method have to attach all events required by this plugin
public function attach(EventManagerInterface $events)
{
$this->handles[] = $events->attach(’getItem.pre’, array($this, ’onGetItemPre’));
$this->handles[] = $events->attach(’getItem.post’, array($this, ’onGetItemPost’));
return $this;
}

10
11
12
13
14
15
16
17

// This method have to detach all events required by this plugin
public function detach(EventManagerInterface $events)
{
foreach ($this->handles as $handle) {
$events->detach($handle);
}
$this->handles = array();
return $this;
}

18
19
20
21
22
23
24
25
26
27

public function onGetItemPre(Event $event)
{
$params = $event->getParams();
echo sprintf("Method ’getItem’ with key ’%s’ startedn", params[’key’]);
}

28
29
30
31
32
33

public function onGetItemPost(Event $event)
{
$params = $event->getParams();
echo sprintf("Method ’getItem’ with key ’%s’ finishedn", params[’key’]);
}

34
35
36
37
38
39

}

40
41
42
43

// After defining this basic plugin we can instantiate and add it to an adapter instance
$plugin = new MyPlugin();
$cache->addPlugin($plugin);

44
45
46
47
48

// Now on calling getItem our basic plugin should print the expected output
$cache->getItem(’cache-key’);
// Method ’getItem’ with key ’cache-key’ started
// Method ’getItem’ with key ’cache-key’ finished

188

Chapter 38. ZendCacheStoragePlugin
CHAPTER 39

ZendCachePattern

39.1 Overview
Cache patterns are configurable objects to solve known performance bottlenecks. Each should be used only in the specific situations they are designed to address. For example you can use one of the CallbackCache, ObjectCache
or ClassCache patterns to cache method and function calls; to cache output generation, the OutputCache pattern
could assist.
All cache patterns implement the same interface, ZendCachePatternPatternInterface, and most extend the abstract class ZendCachePatternAbstractPattern to implement basic logic.
Configuration is provided via the ZendCachePatternPatternOptions class, which can simply be instantiated with an associative array of options passed to the constructor. To configure a pattern object, you can set an
instance of ZendCachePatternPatternOptions with setOptions, or provide your options (either as
an associative array or PatternOptions instance) as the second argument to the factory.
It’s also possible to use a single instance of ZendCachePatternPatternOptions and pass it to multiple
pattern objects.

39.2 Quick Start
Pattern objects can either be created from the provided ZendCachePatternFactory factory, or, by simply
instantiating one of the ZendCachePattern*Cache classes.
1
2
3
4

1
2
3
4
5

// Via the factory:
$callbackCache = ZendCachePatternFactory::factory(’callback’, array(
’storage’ => ’apc’,
));
// OR, the equivalent manual instantiation:
$callbackCache = new ZendCachePatternCallbackCache();
$callbackCache->setOptions(new ZendCachePatternPatternOptions(array(
’storage’ => ’apc’,
)));

189
Zend Framework 2 Documentation, Release 2.2.5

39.3 Available Methods
The following methods are implemented by ZendCachePatternAbstractPattern. Please read documentation of specific patterns to get more information.
setOptions(ZendCachePatternPatternOptions $options)
Set pattern options.
Return type ZendCachePatternPatternInterface
getOptions()
Get all pattern options.
Return type ZendCachePatternPatternOptions

190

Chapter 39. ZendCachePattern
CHAPTER 40

ZendCachePatternCallbackCache

40.1 Overview
The callback cache pattern caches calls of non specific functions and methods given as a callback.

40.2 Quick Start
For instantiation you can use the PatternFactory or do it manual:
1
2

use ZendCachePatternFactory;
use ZendCachePatternPatternOptions;

3
4
5
6
7
8

// Via the factory:
$callbackCache = PatternFactory::factory(’callback’, array(
’storage’
=> ’apc’,
’cache_output’ => true,
));

9
10
11
12
13
14
15

// OR, the equivalent manual instantiation:
$callbackCache = new ZendCachePatternCallbackCache();
$callbackCache->setOptions(new PatternOptions(array(
’storage’
=> ’apc’,
’cache_output’ => true,
)));

40.3 Configuration Options
Option
storage

Data Type

string array
ZendCacheStorageStorageInterface
cache_outputboolean

Default
Value
<none>
true

Description
The storage to write/read
cached data
Cache output of callback

191
Zend Framework 2 Documentation, Release 2.2.5

40.4 Available Methods
call(callable $callback, array $args = array())
Call the specified callback or get the result from cache.
Return type mixed
__call(string $function, array $args)
Function call handler.
Return type mixed
generateKey(callable $callback, array $args = array())
Generate a unique key in base of a key representing the callback part and a key representing the arguments part.
Return type string
setOptions(ZendCachePatternPatternOptions $options)
Set pattern options.
Return type ZendCachePatternCallbackCache
getOptions()
Get all pattern options.
Return type ZendCachePatternPatternOptions

40.5 Examples
Instantiating the callback cache pattern

1

use ZendCachePatternFactory;

2
3
4
5

$callbackCache = PatternFactory::factory(’callback’, array(
’storage’ => ’apc’
));

192

Chapter 40. ZendCachePatternCallbackCache
CHAPTER 41

ZendCachePatternClassCache

41.1 Overview
The ClassCache pattern is an extension to the CallbackCache pattern. It has the same methods but instead it
generates the internally used callback in base of the configured class name and the given method name.

41.2 Quick Start
Instantiating the class cache pattern
1

use ZendCachePatternFactory;

2
3
4
5
6

$classCache = PatternFactory::factory(’class’, array(
’class’
=> ’MyClass’,
’storage’ => ’apc’
));

41.3 Configuration Options
Option

Data Type

Default
Value
storage
string array
<none>
ZendCacheStorageStorageInterface
class
string
<none>
cache_output
boolean
true
cache_by_default boolean
true
class_cache_methods
array
[]
class_non_cache_methods
array

[]

Description
The storage to write/read cached data
The class name
Cache output of callback
Cache method calls by default
List of methods to cache (If
cache_by_default is disabled)
List of methods to no-cache (If
cache_by_default is enabled)

193
Zend Framework 2 Documentation, Release 2.2.5

41.4 Available Methods
call(string $method, array $args = array())
Call the specified method of the configured class.
Return type mixed
__call(string $method, array $args)
Call the specified method of the configured class.
Return type mixed
__set(string $name, mixed $value)
Set a static property of the configured class.
Return type void
__get(string $name)
Get a static property of the configured class.
Return type mixed
__isset(string $name)
Checks if a static property of the configured class exists.
Return type boolean
__unset(string $name)
Unset a static property of the configured class.
Return type void
generateKey(string $method, array $args = array())
Generate a unique key in base of a key representing the callback part and a key representing the arguments part.
Return type string
setOptions(ZendCachePatternPatternOptions $options)
Set pattern options.
Return type ZendCachePatternClassCache
getOptions()
Get all pattern options.
Return type ZendCachePatternPatternOptions

41.5 Examples
Caching of import feeds

194

Chapter 41. ZendCachePatternClassCache
Zend Framework 2 Documentation, Release 2.2.5

1
2
3

$cachedFeedReader = ZendCachePatternFactory::factory(’class’, array(
’class’
=> ’ZendFeedReaderReader’,
’storage’ => ’apc’,

4

// The feed reader doesn’t output anything
// so the output don’t need to be caught and cached
’cache_output’ => false,

5
6
7
8

));

9
10
11
12

$feed = $cachedFeedReader->call("import", array(’http://www.planet-php.net/rdf/’));
// OR
$feed = $cachedFeedReader->import(’http://www.planet-php.net/rdf/’);

41.5. Examples

195
Zend Framework 2 Documentation, Release 2.2.5

196

Chapter 41. ZendCachePatternClassCache
CHAPTER 42

ZendCachePatternObjectCache

42.1 Overview
The ObjectCache pattern is an extension to the CallbackCache pattern. It has the same methods but instead it
generates the internally used callback in base of the configured object and the given method name.

42.2 Quick Start
Instantiating the object cache pattern
1

use ZendCachePatternFactory;

2
3
4
5
6
7

$object
= new stdClass();
$objectCache = PatternFactory::factory(’object’, array(
’object’ => $object,
’storage’ => ’apc’
));

197
Zend Framework 2 Documentation, Release 2.2.5

42.3 Configuration Options
Option

Data Type

storage
object
object_key

Default
Value
string array
<none>
ZendCacheStorageStorageInterface
object
<none>
null string
<Class name
of object>
boolean
true
boolean
true
array
[]

cache_output
cache_by_default
object_cache_methods
obarray
ject_non_cache_methods
obboolean
ject_cache_magic_properties

[]
false

Description
The storage to write/read cached
data
The object to cache methods calls of
A hopefully unique key of the object
Cache output of callback
Cache method calls by default
List of methods to cache (If
cache_by_default is disabled)
List of methods to no-cache (If
cache_by_default is enabled)
Cache calls of magic object
properties

42.4 Available Methods
call(string $method, array $args = array())
Call the specified method of the configured object.
Return type mixed
__call(string $method, array $args)
Call the specified method of the configured object.
Return type mixed
__set(string $name, mixed $value)
Set a property of the configured object.
Return type void
__get(string $name)
Get a property of the configured object.
Return type mixed
__isset(string $name)
Checks if static property of the configured object exists.
Return type boolean
__unset(string $name)
Unset a property of the configured object.
Return type void

198

Chapter 42. ZendCachePatternObjectCache
Zend Framework 2 Documentation, Release 2.2.5

generateKey(string $method, array $args = array())
Generate a unique key in base of a key representing the callback part and a key representing the arguments part.
Return type string
setOptions(ZendCachePatternPatternOptions $options)
Set pattern options.
Return type ZendCachePatternObjectCache
getOptions()
Get all pattern options.
Return type ZendCachePatternPatternOptions

42.5 Examples
Caching a filter

1
2
3
4
5

$filter
= new ZendFilterRealPath();
$cachedFilter = ZendCachePatternFactory::factory(’object’, array(
’object’
=> $filter,
’object_key’ => ’RealpathFilter’,
’storage’
=> ’apc’,

6

// The realpath filter doesn’t output anything
// so the output don’t need to be caught and cached
’cache_output’ => false,

7
8
9
10

));

11
12
13
14

$path = $cachedFilter->call("filter", array(’/www/var/path/../../mypath’));
// OR
$path = $cachedFilter->filter(’/www/var/path/../../mypath’);

42.5. Examples

199
Zend Framework 2 Documentation, Release 2.2.5

200

Chapter 42. ZendCachePatternObjectCache
CHAPTER 43

ZendCachePatternOutputCache

43.1 Overview
The OutputCache pattern caches output between calls to start() and end().

43.2 Quick Start
Instantiating the output cache pattern
1

use ZendCachePatternFactory;

2
3
4
5

$outputCache = PatternFactory::factory(’output’, array(
’storage’ => ’apc’
));

43.3 Configuration Options
Option
storage

Data Type
string array
ZendCacheStorageStorageInterface

Default
Value
<none>

Description
The storage to write/read
cached data

43.4 Available Methods
start(string $key)
If there is a cached item with the given key display it’s data and return true else start buffering output until
end() is called or the script ends and return false.
Return type boolean
end()
Stops buffering output, write buffered data to cache using the given key on start() and displays the buffer.

201
Zend Framework 2 Documentation, Release 2.2.5

Return type boolean
setOptions(ZendCachePatternPatternOptions $options)
Set pattern options.
Return type ZendCachePatternOutputCache
getOptions()
Get all pattern options.
Return type ZendCachePatternPatternOptions

43.5 Examples
Caching simple view scripts

1
2
3

$outputCache = ZendCachePatternFactory::factory(’output’, array(
’storage’ => ’apc’,
));

4
5
6
7

$outputCache->start(’mySimpleViewScript’);
include ’/path/to/view/script.phtml’;
$outputCache->end();

202

Chapter 43. ZendCachePatternOutputCache
CHAPTER 44

ZendCachePatternCaptureCache

44.1 Overview
The CaptureCache pattern is useful to auto-generate static resources in base of a HTTP request. The Webserver
needs to be configured to run a PHP script generating the requested resource so further requests for the same resource
can be shipped without calling PHP again.
It comes with basic logic to manage generated resources.

44.2 Quick Start
Simplest usage as Apache-404 handler
1
2

1
2
3
4
5

# .htdocs
ErrorDocument 404 /index.php
// index.php
use ZendCachePatternFactory;
$capture = ZendCachePatternFactory::factory(’capture’, array(
’public_dir’ => __DIR__,
));

6
7
8

// Start capturing all output excl. headers and write to public directory
$capture->start();

9
10
11

// Don’t forget to change HTTP response code
header(’Status: 200’, true, 200);

12
13

// do stuff to dynamically generate output

203
Zend Framework 2 Documentation, Release 2.2.5

44.3 Configuration Options
Option
public_dir
index_filename
file_locking
file_permission
dir_permission
umask

Data Type
string
string
boolean
integer boolean
integer boolean
integer boolean

Default Value
<none>
“index.html”
true
0600 (false on win)
0700 (false on win)
false

Description
Location of public directory to write output to
The name of the first file if only a directory was requested
Locking output files on writing
Set permissions of generated output files
Set permissions of generated output directories
Using umask on generating output files / directories

44.4 Available Methods
start(string|null $pageId = null)
Start capturing output.
Return type void
set(string $content, string|null $pageId = null)
Write content to page identity.
Return type void
get(string|null $pageId = null)
Get content of an already cached page.
Return type string|false
has(string|null $pageId = null)
Check if a page has been created.
Return type boolean
remove(string|null $pageId = null)
Remove a page.
Return type boolean
clearByGlob(string $pattern = ‘**’)
Clear pages matching glob pattern.
Return type void
setOptions(ZendCachePatternPatternOptions $options)
Set pattern options.
Return type ZendCachePatternCaptureCache
getOptions()
Get all pattern options.
Return type ZendCachePatternPatternOptions

204

Chapter 44. ZendCachePatternCaptureCache
Zend Framework 2 Documentation, Release 2.2.5

44.5 Examples
Scaling images in base of request

1
2

1
2
3
4

# .htdocs
ErrorDocument 404 /index.php
// index.php
$captureCache = ZendCachePatternFactory::factory(’capture’, array(
’public_dir’ => __DIR__,
));

5
6

// TODO

44.5. Examples

205
Zend Framework 2 Documentation, Release 2.2.5

206

Chapter 44. ZendCachePatternCaptureCache
CHAPTER 45

Introduction to ZendCaptcha

CAPTCHA stands for “Completely Automated Public Turing test to tell Computers and Humans Apart”; it is used as
a challenge-response to ensure that the individual submitting information is a human and not an automated process.
Typically, a captcha is used with form submissions where authenticated users are not necessary, but you want to prevent
spam submissions.
Captchas can take a variety of forms, including asking logic questions, presenting skewed fonts, and presenting multiple images and asking how they relate. The ZendCaptcha component aims to provide a variety of back ends that
may be utilized either standalone or in conjunction with the ZendForm component.

207
Zend Framework 2 Documentation, Release 2.2.5

208

Chapter 45. Introduction to ZendCaptcha
CHAPTER 46

Captcha Operation

All CAPTCHA adapters implement ZendCaptchaAdapterInterface, which looks like the following:
1

namespace ZendCaptcha;

2
3

use ZendValidatorValidatorInterface;

4
5
6
7

interface AdapterInterface extends ValidatorInterface
{
public function generate();

8

public function setName($name);

9
10

public function getName();

11
12

// Get helper name used for rendering this captcha type
public function getHelperName();

13
14
15

}

The name setter and getter are used to specify and retrieve the CAPTCHA identifier. The most interesting methods are
generate() and render(). generate() is used to create the CAPTCHA token. This process typically will
store the token in the session so that you may compare against it in subsequent requests. render() is used to render
the information that represents the CAPTCHA, be it an image, a figlet, a logic problem, or some other CAPTCHA.
A simple use case might look like the following:
1
2
3
4
5
6

// Originating request:
$captcha = new ZendCaptchaFiglet(array(
’name’ => ’foo’,
’wordLen’ => 6,
’timeout’ => 300,
));

7
8

$id = $captcha->generate();

9
10
11

//this will output a Figlet string
echo $captcha->getFiglet()->render($captcha->getWord());

12
13
14
15

// On a subsequent request:
// Assume a captcha setup as before, with corresponding form fields, the value of $_POST[’foo’]

209
Zend Framework 2 Documentation, Release 2.2.5

16
17
18
19

// would be key/value array: id => captcha ID, input => captcha value
if ($captcha->isValid($_POST[’foo’], $_POST)) {
// Validated!
}

Note: Under most circumstances, you probably prefer the use of ZendCaptcha functionality combined with the
power of the ZendForm component. For an example on how to use ZendFormElementCaptcha, have a
look at the ZendForm Quick Start.

210

Chapter 46. Captcha Operation
CHAPTER 47

CAPTCHA Adapters

The following adapters are shipped with Zend Framework by default.

47.1 ZendCaptchaAbstractWord
ZendCaptchaAbstractWord is an abstract adapter that serves as the base class for most other CAPTCHA
adapters. It provides mutators for specifying word length, session TTL and the session container object to use.
ZendCaptchaAbstractWord also encapsulates validation logic.
By default, the word length is 8 characters, the session timeout is 5 minutes, and ZendSessionContainer is
used for persistence (using the namespace “ZendFormCaptcha<captcha ID>”).
In addition to the methods required by the ZendCaptchaAdapterInterface
ZendCaptchaAbstractWord exposes the following methods:

interface,

• setWordLen($length) and getWordLen() allow you to specify the length of the generated “word” in
characters, and to retrieve the current value.
• setTimeout($ttl) and getTimeout() allow you to specify the time-to-live of the session token, and to
retrieve the current value. $ttl should be specified in seconds.
• setUseNumbers($numbers) and getUseNumbers() allow you to specify if numbers will be considered as possible characters for the random work or only letters would be used.
• setSessionClass($class) and getSessionClass() allow you to specify an alternate
ZendSessionContainer implementation to use to persist the CAPTCHA token and to retrieve
the current value.
• getId() allows you to retrieve the current token identifier.
• getWord() allows you to retrieve the generated word to use with the CAPTCHA. It will generate the word for
you if none has been generated yet.
• setSession(ZendSessionContainer $session) allows you to specify a session object to use
for persisting the CAPTCHA token. getSession() allows you to retrieve the current session object.
All word CAPTCHAs allow you to pass an array of options or Traversable object to the constructor, or, alternately,
pass them to setOptions(). By default, the wordLen, timeout, and sessionClass keys may all be used. Each
concrete implementation may define additional keys or utilize the options in other ways.

211
Zend Framework 2 Documentation, Release 2.2.5

Note: ZendCaptchaAbstractWord is an abstract class and may not be instantiated directly.

47.2 ZendCaptchaDumb
The ZendCaptchaDumb adapter is mostly self-descriptive. It provides a random string that must be typed in
reverse to validate. As such, it’s not a good CAPTCHA solution and should only be used for testing. It extends
ZendCaptchaAbstractWord.

47.3 ZendCaptchaFiglet
The ZendCaptchaFiglet adapter utilizes ZendTextFiglet to present a figlet to the user.
Options passed to the constructor will also be passed to the ZendTextFiglet object. See the ZendTextFiglet documentation for details on what configuration options are available.

47.4 ZendCaptchaImage
The ZendCaptchaImage adapter takes the generated word and renders it as an image, performing various skewing permutations to make it difficult to automatically decipher. It requires the GD extension compiled with TrueType
or Freetype support. Currently, the ZendCaptchaImage adapter can only generate PNG images.
ZendCaptchaImage extends ZendCaptchaAbstractWord, and additionally exposes the following
methods:
• setExpiration($expiration) and getExpiration() allow you to specify a maximum lifetime the
CAPTCHA image may reside on the filesystem. This is typically a longer than the session lifetime. Garbage
collection is run periodically each time the CAPTCHA object is invoked, deleting all images that have expired.
Expiration values should be specified in seconds.
• setGcFreq($gcFreq) and getGcFreg() allow you to specify how frequently garbage collection should
run. Garbage collection will run every 1/$gcFreq calls. The default is 100.
• setFont($font) and getFont() allow you to specify the font you will use. $font should be a fully
qualified path to the font file. This value is required; the CAPTCHA will throw an exception during generation
if the font file has not been specified.
• setFontSize($fsize) and getFontSize() allow you to specify the font size in pixels for generating
the CAPTCHA. The default is 24px.
• setHeight($height) and getHeight() allow you to specify the height in pixels of the generated
CAPTCHA image. The default is 50px.
• setWidth($width) and getWidth() allow you to specify the width in pixels of the generated CAPTCHA
image. The default is 200px.
• setImgDir($imgDir) and getImgDir() allow you to specify the directory for storing CAPTCHA images. The default is “./images/captcha/”, relative to the bootstrap script.
• setImgUrl($imgUrl) and getImgUrl() allow you to specify the relative path to a CAPTCHA image to
use for HTML markup. The default is “/images/captcha/”.
• setSuffix($suffix) and getSuffix() allow you to specify the filename suffix for the CAPTCHA
image. The default is “.png”. Note: changing this value will not change the type of the generated image.

212

Chapter 47. CAPTCHA Adapters
Zend Framework 2 Documentation, Release 2.2.5

• setDotNoiseLevel($level)
and
getDotNoiseLevel(),
along
with
setLineNoiseLevel($level) and getLineNoiseLevel(), allow you to control how much
“noise” in the form of random dots and lines the image would contain. Each unit of $level produces one
random dot or line. The default is 100 dots and 5 lines. The noise is added twice - before and after the image
distortion transformation.
All of the above options may be passed to the constructor by simply removing the ‘set’ method prefix and casting the
initial letter to lowercase: “suffix”, “height”, “imgUrl”, etc.

47.5 ZendCaptchaReCaptcha
The ZendCaptchaReCaptcha adapter uses ZendServiceReCaptchaReCaptcha to generate and validate
CAPTCHAs. It exposes the following methods:
• setPrivKey($key) and getPrivKey() allow you to specify the private key to use for the ReCaptcha
service. This must be specified during construction, although it may be overridden at any point.
• setPubKey($key) and getPubKey() allow you to specify the public key to use with the ReCaptcha
service. This must be specified during construction, although it may be overridden at any point.
• setService(ZendServiceReCaptchaReCaptcha $service) and getService() allow you
to set and get the ReCaptcha service object.

47.5. ZendCaptchaReCaptcha

213
Zend Framework 2 Documentation, Release 2.2.5

214

Chapter 47. CAPTCHA Adapters
CHAPTER 48

Introduction to ZendConfig

ZendConfig is designed to simplify access to configuration data within applications. It provides a nested object
property-based user interface for accessing this configuration data within application code. The configuration data
may come from a variety of media supporting hierarchical data storage. Currently, ZendConfig provides adapters
that read and write configuration data stored in .ini, JSON, YAML and XML files.

48.1 Using ZendConfigConfig with a Reader Class
Normally, it is expected that users would use one of the reader classes to read a configuration file, but if configuration
data are available in a PHP array, one may simply pass the data to ZendConfigConfig‘s constructor in order
to utilize a simple object-oriented interface:
1
2
3
4
5
6
7
8
9
10
11
12
13

// An array of configuration data is given
$configArray = array(
’webhost’ => ’www.example.com’,
’database’ => array(
’adapter’ => ’pdo_mysql’,
’params’ => array(
’host’
=> ’db.example.com’,
’username’ => ’dbuser’,
’password’ => ’secret’,
’dbname’
=> ’mydatabase’
)
)
);

14
15
16

// Create the object-oriented wrapper using the configuration data
$config = new ZendConfigConfig($configArray);

17
18
19

// Print a configuration datum (results in ’www.example.com’)
echo $config->webhost;

As illustrated in the example above, ZendConfigConfig provides nested object property syntax to access configuration data passed to its constructor.
Along with the object-oriented access to the data values, ZendConfigConfig also has get() method that
returns the supplied value if the data element doesn’t exist in the configuration array. For example:

215
Zend Framework 2 Documentation, Release 2.2.5

1

$host = $config->database->get(’host’, ’localhost’);

48.2 Using ZendConfigConfig with a PHP Configuration File
It is often desirable to use a purely PHP-based configuration file. The following code illustrates how easily this can be
accomplished:
1
2
3
4
5
6
7
8
9
10
11
12
13

1
2

// config.php
return array(
’webhost’ => ’www.example.com’,
’database’ => array(
’adapter’ => ’pdo_mysql’,
’params’ => array(
’host’
=> ’db.example.com’,
’username’ => ’dbuser’,
’password’ => ’secret’,
’dbname’
=> ’mydatabase’
)
)
);
// Consumes the configuration array
$config = new ZendConfigConfig(include ’config.php’);

3
4
5

// Print a configuration datum (results in ’www.example.com’)
echo $config->webhost;

216

Chapter 48. Introduction to ZendConfig
CHAPTER 49

Theory of Operation

Configuration data are made accessible to ZendConfigConfig‘s constructor with an associative array, which
may be multi-dimensional, so data can be organized from general to specific. Concrete adapter classes adapt configuration data from storage to produce the associative array for ZendConfigConfig‘s constructor. If needed, user
scripts may provide such arrays directly to ZendConfigConfig‘s constructor, without using a reader class.
Each value in the configuration data array becomes a property of the ZendConfigConfig object. The key
is used as the property name. If a value is itself an array, then the resulting object property is created as a new
ZendConfigConfig object, loaded with the array data. This occurs recursively, such that a hierarchy of configuration data may be created with any number of levels.
ZendConfigConfig implements the Countable and Iterator interfaces in order to facilitate simple access to
configuration data. Thus, ZendConfigConfig objects support the count() function and PHP constructs such as
foreach.
By default, configuration data made available through ZendConfigConfig are read-only, and an assignment
(e.g. $config->database->host = ’example.com’;) results in a thrown exception. This default behavior may be overridden through the constructor, allowing modification of data values. Also, when modifications are
allowed, ZendConfigConfig supports unsetting of values (i.e. unset($config->database->host)).
The isReadOnly() method can be used to determine if modifications to a given ZendConfigConfig
object are allowed and the setReadOnly() method can be used to stop any further modifications to a
ZendConfigConfig object that was created allowing modifications.
Note: Modifying Config does not save changes
It is important not to confuse such in-memory modifications with saving configuration data out to specific storage
media. Tools for creating and modifying configuration data for various storage media are out of scope with respect
to ZendConfigConfig. Third-party open source solutions are readily available for the purpose of creating and
modifying configuration data for various storage media.
If you have two ZendConfigConfig objects, you can merge them into a single object using the merge() function. For example, given $config and $localConfig, you can merge data from $localConfig to $config
using $config->merge($localConfig);. The items in $localConfig will override any items with the
same name in $config.
Note: The ZendConfigConfig object that is performing the merge must have been constructed to allow
modifications, by passing TRUE as the second parameter of the constructor. The setReadOnly() method can then
be used to prevent any further modifications after the merge is complete.

217
Zend Framework 2 Documentation, Release 2.2.5

218

Chapter 49. Theory of Operation
CHAPTER 50

ZendConfigReader

ZendConfigReader gives you the ability to read a config file. It works with concrete implementations for
different file format. The ZendConfigReader is only an interface, that define the two methods fromFile()
and fromString(). The concrete implementations of this interface are:
• ZendConfigReaderIni
• ZendConfigReaderXml
• ZendConfigReaderJson
• ZendConfigReaderYaml
The fromFile() and fromString() return a PHP array contains the data of the configuration file.
Note: Differences from ZF1
The ZendConfigReader component no longer supports the following features:
• Inheritance of sections.
• Reading of specific sections.

50.1 ZendConfigReaderIni
ZendConfigReaderIni enables developers to store configuration data in a familiar INI format and read them
in the application by using an array syntax.
ZendConfigReaderIni utilizes the parse_ini_file() PHP function. Please review this documentation to be
aware of its specific behaviors, which propagate to ZendConfigReaderIni, such as how the special values
of “TRUE”, “FALSE”, “yes”, “no”, and “NULL” are handled.
Note: Key Separator
By default, the key separator character is the period character (“.”). This can be changed, however, using the
setNestSeparator() method. For example:
1
2

$reader = new ZendConfigReaderIni();
$reader->setNestSeparator(’-’);

219
Zend Framework 2 Documentation, Release 2.2.5

The following example illustrates a basic use of ZendConfigReaderIni for loading configuration data from
an INI file. In this example there are configuration data for both a production system and for a staging system. Suppose
we have the following INI configuration file:
1
2
3
4
5
6

webhost
database.adapter
database.params.host
database.params.username
database.params.password
database.params.dbname

=
=
=
=
=
=

’www.example.com’
’pdo_mysql’
’db.example.com’
’dbuser’
’secret’
’dbproduction’

We can use the ZendConfigReaderIni to read this INI file:
1
2

$reader = new ZendConfigReaderIni();
$data
= $reader->fromFile(’/path/to/config.ini’);

3
4
5

echo $data[’webhost’] // prints "www.example.com"
echo $data[’database’][’params’][’dbname’]; // prints "dbproduction"

The ZendConfigReaderIni supports a feature to include the content of a INI file in a specific section of
another INI file. For instance, suppose we have an INI file with the database configuration:
1
2
3
4
5

database.adapter
database.params.host
database.params.username
database.params.password
database.params.dbname

=
=
=
=
=

’pdo_mysql’
’db.example.com’
’dbuser’
’secret’
’dbproduction’

We can include this configuration in another INI file, for instance:
1
2

webhost = ’www.example.com’
@include = ’database.ini’

If we read this file using the component ZendConfigReaderIni we will obtain the same configuration data
structure of the previous example.
The @include = ’file-to-include.ini’ can be used also in a subelement of a value. For instance we can
have an INI file like that:
1
2
3
4
5

adapter
params.host
params.username
params.password
params.dbname

=
=
=
=
=

’pdo_mysql’
’db.example.com’
’dbuser’
’secret’
’dbproduction’

And assign the @include as subelement of the database value:
1
2

webhost
= ’www.example.com’
database.@include = ’database.ini’

50.2 ZendConfigReaderXml
ZendConfigReaderXml enables developers to read configuration data in a familiar XML format and read
them in the application by using an array syntax. The root element of the XML file or string is irrelevant and may be
named arbitrarily.

220

Chapter 50. ZendConfigReader
Zend Framework 2 Documentation, Release 2.2.5

The following example illustrates a basic use of ZendConfigReaderXml for loading configuration data from
an XML file. Suppose we have the following XML configuration file:
1
2
3
4
5
6
7
8
9
10
11
12
13

<?xml version="1.0" encoding="utf-8"?>?>
<config>
<webhost>www.example.com</webhost>
<database>
<adapter value="pdo_mysql"/>
<params>
<host value="db.example.com"/>
<username value="dbuser"/>
<password value="secret"/>
<dbname value="dbproduction"/>
</params>
</database>
</config>

We can use the ZendConfigReaderXml to read this XML file:
1
2

$reader = new ZendConfigReaderXml();
$data
= $reader->fromFile(’/path/to/config.xml’);

3
4
5

echo $data[’webhost’] // prints "www.example.com"
echo $data[’database’][’params’][’dbname’]; // prints "dbproduction"

ZendConfigReaderXml utilizes the XMLReader PHP class. Please review this documentation to be aware
of its specific behaviors, which propagate to ZendConfigReaderXml.
Using ZendConfigReaderXml we can include the content of XML files in a specific XML element. This
is provided using the standard function XInclude of XML. To use this function you have to add the namespace
xmlns:xi="http://www.w3.org/2001/XInclude" to the XML file. Suppose we have an XML files that
contains only the database configuration:
1
2
3
4
5
6
7
8
9
10
11
12

<?xml version="1.0" encoding="utf-8"?>
<config>
<database>
<adapter>pdo_mysql</adapter>
<params>
<host>db.example.com</host>
<username>dbuser</username>
<password>secret</password>
<dbname>dbproduction</dbname>
</params>
</database>
</config>

We can include this configuration in another XML file, for instance:
1
2
3
4
5

<?xml version="1.0" encoding="utf-8"?>
<config xmlns:xi="http://www.w3.org/2001/XInclude">
<webhost>www.example.com</webhost>
<xi:include href="database.xml"/>
</config>

The syntax to include an XML file in a specific element is <xi:include href="file-to-include.xml"/>

50.2. ZendConfigReaderXml

221
Zend Framework 2 Documentation, Release 2.2.5

50.3 ZendConfigReaderJson
ZendConfigReaderJson enables developers to read configuration data in a JSON format and read them in
the application by using an array syntax.
The following example illustrates a basic use of ZendConfigReaderJson for loading configuration data from
a JSON file. Suppose we have the following JSON configuration file:
1

{
"webhost" : "www.example.com",
"database" : {
"adapter" : "pdo_mysql",
"params" : {
"host"
: "db.example.com",
"username" : "dbuser",
"password" : "secret",
"dbname"
: "dbproduction"
}
}

2
3
4
5
6
7
8
9
10
11
12

}

We can use the ZendConfigReaderJson to read this JSON file:
1
2

$reader = new ZendConfigReaderJson();
$data
= $reader->fromFile(’/path/to/config.json’);

3
4
5

echo $data[’webhost’] // prints "www.example.com"
echo $data[’database’][’params’][’dbname’]; // prints "dbproduction"

ZendConfigReaderJson utilizes the ZendJsonJson class.
Using ZendConfigReaderJson we can include the content of a JSON file in a specific JSON section or
element. This is provided using the special syntax @include. Suppose we have a JSON file that contains only the
database configuration:
1

{
"database" : {
"adapter" : "pdo_mysql",
"params" : {
"host"
: "db.example.com",
"username" : "dbuser",
"password" : "secret",
"dbname"
: "dbproduction"
}
}

2
3
4
5
6
7
8
9
10
11

}

We can include this configuration in another JSON file, for instance:
1

{
"webhost" : "www.example.com",
"@include" : "database.json"

2
3
4

}

222

Chapter 50. ZendConfigReader
Zend Framework 2 Documentation, Release 2.2.5

50.4 ZendConfigReaderYaml
ZendConfigReaderYaml enables developers to read configuration data in a YAML format and read them in
the application by using an array syntax. In order to use the YAML reader we need to pass a callback to an external
PHP library or use the Yaml PECL extension.
The following example illustrates a basic use of ZendConfigReaderYaml that use the Yaml PECL extension.
Suppose we have the following YAML configuration file:
1
2
3
4
5
6
7
8

webhost: www.example.com
database:
adapter: pdo_mysql
params:
host:
db.example.com
username: dbuser
password: secret
dbname:
dbproduction

We can use the ZendConfigReaderYaml to read this YAML file:
1
2

$reader = new ZendConfigReaderYaml();
$data
= $reader->fromFile(’/path/to/config.yaml’);

3
4
5

echo $data[’webhost’] // prints "www.example.com"
echo $data[’database’][’params’][’dbname’]; // prints "dbproduction"

If you want to use an external YAML reader you have to pass the callback function in the constructor of the class. For
instance, if you want to use the Spyc library:
1
2

// include the Spyc library
require_once (’path/to/spyc.php’);

3
4
5

$reader = new ZendConfigReaderYaml(array(’Spyc’,’YAMLLoadString’));
$data
= $reader->fromFile(’/path/to/config.yaml’);

6
7
8

echo $data[’webhost’] // prints "www.example.com"
echo $data[’database’][’params’][’dbname’]; // prints "dbproduction"

You can also instantiate the ZendConfigReaderYaml without any parameter and specify the YAML reader
in a second moment using the setYamlDecoder() method.
Using ZendConfigReaderYaml we can include the content of a YAML file in a specific YAML section or
element. This is provided using the special syntax @include. Suppose we have a YAML file that contains only the
database configuration:
1
2
3
4
5
6
7

database:
adapter: pdo_mysql
params:
host:
db.example.com
username: dbuser
password: secret
dbname:
dbproduction

We can include this configuration in another YAML file, for instance:
webhost: www.example.com
@include: database.yaml

50.4. ZendConfigReaderYaml

223
Zend Framework 2 Documentation, Release 2.2.5

224

Chapter 50. ZendConfigReader
CHAPTER 51

ZendConfigWriter

ZendConfigWriter gives you the ability to write config files out of array, ZendConfigConfig and
any Traversable object. The ZendConfigWriter is an interface that defines two methods: toFile() and
toString(). We have five specific writers that implement this interface:
• ZendConfigWriterIni
• ZendConfigWriterXml
• ZendConfigWriterPhpArray
• ZendConfigWriterJson
• ZendConfigWriterYaml

51.1 ZendConfigWriterIni
The INI writer has two modes for rendering with regard to sections. By default the top-level configuration is always
written into section names. By calling $writer->setRenderWithoutSectionsFlags(true); all options
are written into the global namespace of the INI file and no sections are applied.
As an addition ZendConfigWriterIni has an additional option parameter nestSeparator, which defines with which character the single nodes are separated. The default is a single dot, like it is accepted by
ZendConfigReaderIni by default.
When modifying or creating a ZendConfigConfig object, there are some things to know. To create or modify
a value, you simply say set the parameter of the Config object via the parameter accessor (->). To create a section in
the root or to create a branch, you just create a new array (“$config->branch = array();”).
Using ZendConfigWriterIni

This example illustrates the basic use of ZendConfigWriterIni to create a new config file:
1
2
3

// Create the config object
$config = new ZendConfigConfig(array(), true);
$config->production = array();

4
5
6

$config->production->webhost = ’www.example.com’;
$config->production->database = array();

225
Zend Framework 2 Documentation, Release 2.2.5

7
8
9
10
11

$config->production->database->params = array();
$config->production->database->params->host = ’localhost’;
$config->production->database->params->username = ’production’;
$config->production->database->params->password = ’secret’;
$config->production->database->params->dbname = ’dbproduction’;

12
13
14

$writer = new ZendConfigWriterIni();
echo $writer->toString($config);

The result of this code is an INI string contains the following values:
1
2
3
4
5
6

[production]
webhost = "www.example.com"
database.params.host = "localhost"
database.params.username = "production"
database.params.password = "secret"
database.params.dbname = "dbproduction"

You can use the method toFile() to store the INI data in a file.

51.2 ZendConfigWriterXml
The ZendConfigWriterXml can be used to generate an XML string or file starting from a
ZendConfigConfig object.
Using ZendConfigWriterXml

This example illustrates the basic use of ZendConfigWriterXml to create a new config file:
1
2
3

// Create the config object
$config = new ZendConfigConfig(array(), true);
$config->production = array();

4
5
6
7
8
9
10
11

$config->production->webhost = ’www.example.com’;
$config->production->database = array();
$config->production->database->params = array();
$config->production->database->params->host = ’localhost’;
$config->production->database->params->username = ’production’;
$config->production->database->params->password = ’secret’;
$config->production->database->params->dbname = ’dbproduction’;

12
13
14

$writer = new ZendConfigWriterXml();
echo $writer->toString($config);

The result of this code is an XML string contains the following data:
1
2
3
4
5
6
7
8
9

<?xml version="1.0" encoding="UTF-8"?>
<zend-config>
<production>
<webhost>www.example.com</webhost>
<database>
<params>
<host>localhost</host>
<username>production</username>
<password>secret</password>

226

Chapter 51. ZendConfigWriter
Zend Framework 2 Documentation, Release 2.2.5

10
11
12
13
14

<dbname>dbproduction</dbname>
</params>
</database>
</production>
</zend-config>

You can use the method toFile() to store the XML data in a file.

51.3 ZendConfigWriterPhpArray
The ZendConfigWriterPhpArray can be used to generate a PHP code that returns an array representation
of an ZendConfigConfig object.
Using ZendConfigWriterPhpArray

This example illustrates the basic use of ZendConfigWriterPhpArray to create a new config file:
1
2
3

// Create the config object
$config = new ZendConfigConfig(array(), true);
$config->production = array();

4
5
6
7
8
9
10
11

$config->production->webhost = ’www.example.com’;
$config->production->database = array();
$config->production->database->params = array();
$config->production->database->params->host = ’localhost’;
$config->production->database->params->username = ’production’;
$config->production->database->params->password = ’secret’;
$config->production->database->params->dbname = ’dbproduction’;

12
13
14

$writer = new ZendConfigWriterPhpArray();
echo $writer->toString($config);

The result of this code is a PHP script that returns an array as follow:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

<?php
return array (
’production’ =>
array (
’webhost’ => ’www.example.com’,
’database’ =>
array (
’params’ =>
array (
’host’ => ’localhost’,
’username’ => ’production’,
’password’ => ’secret’,
’dbname’ => ’dbproduction’,
),
),
),
);

You can use the method toFile() to store the PHP script in a file.

51.3. ZendConfigWriterPhpArray

227
Zend Framework 2 Documentation, Release 2.2.5

51.4 ZendConfigWriterJson
The ZendConfigWriterJson can be used to generate a PHP code that returns the JSON representation of a
ZendConfigConfig object.
Using ZendConfigWriterJson

This example illustrates the basic use of ZendConfigWriterJson to create a new config file:
1
2
3

// Create the config object
$config = new ZendConfigConfig(array(), true);
$config->production = array();

4
5
6
7
8
9
10
11

$config->production->webhost = ’www.example.com’;
$config->production->database = array();
$config->production->database->params = array();
$config->production->database->params->host = ’localhost’;
$config->production->database->params->username = ’production’;
$config->production->database->params->password = ’secret’;
$config->production->database->params->dbname = ’dbproduction’;

12
13
14

$writer = new ZendConfigWriterJson();
echo $writer->toString($config);

The result of this code is a JSON string contains the following values:
1
2
3
4
5
6
7
8
9
10

{ "webhost" : "www.example.com",
"database" : {
"params" : {
"host"
: "localhost",
"username" : "production",
"password" : "secret",
"dbname"
: "dbproduction"
}
}
}

You can use the method toFile() to store the JSON data in a file.
The ZendConfigWriterJson class uses the ZendJsonJson component to convert the data in a JSON
format.

51.5 ZendConfigWriterYaml
The ZendConfigWriterYaml can be used to generate a PHP code that returns the YAML representation of
a ZendConfigConfig object. In order to use the YAML writer we need to pass a callback to an external PHP
library or use the Yaml PECL extension.
Using ZendConfigWriterYaml

This example illustrates the basic use of ZendConfigWriterYaml to create a new config file using the Yaml
PECL extension:

228

Chapter 51. ZendConfigWriter
Zend Framework 2 Documentation, Release 2.2.5

1
2
3

// Create the config object
$config = new ZendConfigConfig(array(), true);
$config->production = array();

4
5
6
7
8
9
10
11

$config->production->webhost = ’www.example.com’;
$config->production->database = array();
$config->production->database->params = array();
$config->production->database->params->host = ’localhost’;
$config->production->database->params->username = ’production’;
$config->production->database->params->password = ’secret’;
$config->production->database->params->dbname = ’dbproduction’;

12
13
14

$writer = new ZendConfigWriterYaml();
echo $writer->toString($config);

The result of this code is a YAML string contains the following values:
1
2
3
4
5
6
7

webhost: www.example.com
database:
params:
host:
localhost
username: production
password: secret
dbname:
dbproduction

You can use the method toFile() to store the YAML data in a file.
If you want to use an external YAML writer library you have to pass the callback function in the constructor of the
class. For instance, if you want to use the Spyc library:
1
2

// include the Spyc library
require_once (’path/to/spyc.php’);

3
4
5

$writer = new ZendConfigWriterYaml(array(’Spyc’,’YAMLDump’));
echo $writer->toString($config);

51.5. ZendConfigWriterYaml

229
Zend Framework 2 Documentation, Release 2.2.5

230

Chapter 51. ZendConfigWriter
CHAPTER 52

ZendConfigProcessor

ZendConfigProcessor gives you the ability to perform some operations on a ZendConfigConfig
object.
The ZendConfigProcessor is an interface that defines two methods: process() and
processValue(). These operations are provided by the following concrete implementations:
• ZendConfigProcessorConstant: manage PHP constant values;
• ZendConfigProcessorFilter: filter the configuration data using ZendFilter;
• ZendConfigProcessorQueue: manage a queue of operations to apply to configuration data;
• ZendConfigProcessorToken: find and replace specific tokens;
• ZendConfigProcessorTranslator:
ZendI18nTranslator;

translate configuration values in other languages using

Below we reported some examples for each type of processor.

52.1 ZendConfigProcessorConstant
Using ZendConfigProcessorConstant

This example illustrates the basic use of ZendConfigProcessorConstant:
1
2
3
4

define (’TEST_CONST’, ’bar’);
// set true to ZendConfigConfig to allow modifications
$config = new ZendConfigConfig(array(’foo’ => ’TEST_CONST’), true);
$processor = new ZendConfigProcessorConstant();

5
6
7
8

echo $config->foo . ’,’;
$processor->process($config);
echo $config->foo;

This example returns the output: TEST_CONST, bar..

231
Zend Framework 2 Documentation, Release 2.2.5

52.2 ZendConfigProcessorFilter
Using ZendConfigProcessorFilter

This example illustrates the basic use of ZendConfigProcessorFilter:
1
2
3

use ZendFilterStringToUpper;
use ZendConfigProcessorFilter as FilterProcessor;
use ZendConfigConfig;

4
5
6

$config = new Config(array (’foo’ => ’bar’), true);
$upper = new StringToUpper();

7
8

$upperProcessor = new FilterProcessor($upper);

9
10
11
12

echo $config->foo . ’,’;
$upperProcessor->process($config);
echo $config->foo;

This example returns the output: bar,BAR.

52.3 ZendConfigProcessorQueue
Using ZendConfigProcessorQueue

This example illustrates the basic use of ZendConfigProcessorQueue:
1
2
3
4
5

use
use
use
use
use

ZendFilterStringToLower;
ZendFilterStringToUpper;
ZendConfigProcessorFilter as FilterProcessor;
ZendConfigProcessorQueue;
ZendConfigConfig;

6
7
8
9

$config = new Config(array (’foo’ => ’bar’), true);
$upper = new StringToUpper();
$lower = new StringToLower();

10
11
12

$lowerProcessor = new FilterProcessor($lower);
$upperProcessor = new FilterProcessor($upper);

13
14
15
16
17

$queue = new Queue();
$queue->insert($upperProcessor);
$queue->insert($lowerProcessor);
$queue->process($config);

18
19

echo $config->foo;

This example returns the output: bar. The filters in the queue are applied with a FIFO logic (First In, First Out).

232

Chapter 52. ZendConfigProcessor
Zend Framework 2 Documentation, Release 2.2.5

52.4 ZendConfigProcessorToken
Using ZendConfigProcessorToken

This example illustrates the basic use of ZendConfigProcessorToken:
1
2
3

// set the Config to true to allow modifications
$config = new Config(array(’foo’ => ’Value is TOKEN’), true);
$processor = new TokenProcessor();

4
5
6
7
8

$processor->addToken(’TOKEN’, ’bar’);
echo $config->foo . ’,’;
$processor->process($config);
echo $config->foo;

This example returns the output: Value is TOKEN,Value is bar.

52.5 ZendConfigProcessorTranslator
Using ZendConfigProcessorTranslator

This example illustrates the basic use of ZendConfigProcessorTranslator:
1
2
3

use ZendConfigConfig;
use ZendConfigProcessorTranslator as TranslatorProcessor;
use ZendI18nTranslatorTranslator;

4
5

$config = new Config(array(’animal’ => ’dog’), true);

6
7
8
9
10
11
12
13

/*
* The following mapping would exist for the translation
* loader you provide to the translator instance
* $italian = array(
’dog’ => ’cane’
*
* );
*/

14
15
16
17

$translator = new Translator();
// ... configure the translator ...
$processor = new TranslatorProcessor($translator);

18
19
20
21

echo "English: {$config->animal}, ";
$processor->process($config);
echo "Italian: {$config->animal}";

This example returns the output: English:

52.4. ZendConfigProcessorToken

dog, Italian:

cane.

233
Zend Framework 2 Documentation, Release 2.2.5

234

Chapter 52. ZendConfigProcessor
CHAPTER 53

The Factory

The factory gives you the ability to load a configuration file to an array or to ZendConfigConfig object. The
factory has two purposes
• Loading configuration file(s)
• Storing a configuration file
Note: Storing the configuration will be done to one file. The factory is not aware of merging two or more configurations and will not store it into multiple files. If you want to store particular configuration sections to a different file
you should separate it manually.

53.1 Loading configuration file
The next example illustrates how to load a single configuration file
1
2

//Load a php file as array
$config = ZendConfigFactory::fromFile(__DIR__.’/config/my.config.php’);

3
4
5

//Load a xml file as Config object
$config = ZendConfigFactory::fromFile(__DIR__.’/config/my.config.xml’, true);

For merging multiple configuration files

53.2 Storing configuration file
Sometimes you want to store the configuration to a file. Also this is really easy to do

235
Zend Framework 2 Documentation, Release 2.2.5

236

Chapter 53. The Factory
CHAPTER 54

Introduction to ZendConsole

Zend Framework 2 features built-in console support.
When a ZendApplication is run from a console window (a shell window or Windows command prompt), it will
recognize this fact and prepare ZendMvc components to handle the request. Console support is enabled by default,
but to function properly it requires at least one console route and one action controller to handle the request.
• Console routing allows you to invoke controllers and action depending on command line parameters provided
by the user.
• Module Manager integration allows ZF2 applications and modules to display help and usage information, in
case the command line has not been understood (no route matched).
• Console-aware action controllers will receive a console request containing all named parameters and flags. They
are able to send output back to the console window.
• Console adapters provide a level of abstraction for interacting with console on different operating systems.
• Console prompts can be used to interact with the user by asking him questions and retrieving input.

54.1 Writing console routes
A console route defines required and optional command line parameters. When a route matches, it behaves analogical
to a standard, http route and can point to a MVC controller and an action.
Let’s assume that we’d like our application to handle the following command line:
> zf user resetpassword user@mail.com

When a user runs our application (zf) with these parameters, we’d like to call action resetpassword of
ApplicationControllerIndexController.
Note: We will use zf to depict the entry point for your application, it can be shell script in application bin folder or
simply an alias for php public/index.php
First we need to create a route definition:
user resetpassword <userEmail>

237
Zend Framework 2 Documentation, Release 2.2.5

This simple route definition expects exactly 3 arguments: a literal “user”, literal “resetpassword” followed by a parameter we’re calling “userEmail”. Let’s assume we also accept one optional parameter, that will turn on verbose
operation:
user resetpassword [--verbose|-v] <userEmail>

Now our console route expects the same 3 parameters but will also recognise an optional --verbose flag, or its
shorthand version: -v.
Note: The order of flags is ignored by ZendConsole. Flags can appear before positional parameters, after them
or anywhere in between. The order of multiple flags is also irrelevant. This applies both to route definitions and the
order that flags are used on the command line.
Let’s use the definition above and configure our console route. Console routes are automatically loaded from the
following location inside config file:
1
2
3
4
5
6

array(
’router’ => array(
’routes’ => array(
// HTTP routes are defined here
)
),

7

’console’ => array(
’router’ => array(
’routes’ => array(
// Console routes go here
)
)
),

8
9
10
11
12
13
14
15

)

Let’s create our console route and point it to ApplicationControllerIndexController::resetpasswordAction()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

// we could define routes for ApplicationControllerIndexController in Application module config fil
// which is usually located at modules/application/config/module.config.php
array(
’console’ => array(
’router’ => array(
’routes’ => array(
’user-reset-password’ => array(
’options’ => array(
’route’
=> ’user resetpassword [--verbose|-v] <userEmail>’,
’defaults’ => array(
’controller’ => ’ApplicationControllerIndex’,
’action’
=> ’password’
)
)
)
)
)
)
)

See Also:
To learn more about console routes and how to use them, please read this chapter: Console routes and routing

238

Chapter 54. Introduction to ZendConsole
Zend Framework 2 Documentation, Release 2.2.5

54.2 Handling console requests
When a user runs our application from command line and arguments match our console route, a controller class
will be instantiated and an action method will be called, just like it is with http requests.
We will now add resetpassword action to ApplicationControllerIndexController:
1
2

<?php
namespace ApplicationController;

3
4
5
6
7

use
use
use
use

ZendMvcControllerAbstractActionController;
ZendViewModelViewModel;
ZendConsoleRequest as ConsoleRequest;
ZendMathRand;

8
9
10
11
12
13
14

class IndexController extends AbstractActionController
{
public function indexAction()
{
return new ViewModel(); // display standard index page
}

15

public function resetpasswordAction()
{
$request = $this->getRequest();

16
17
18
19

// Make sure that we are running in a console and the user has not tricked our
// application into running this action from a public web server.
if (!$request instanceof ConsoleRequest){
throw new RuntimeException(’You can only use this action from a console!’);
}

20
21
22
23
24
25

// Get user email from console and check if the user used --verbose or -v flag
$userEmail
= $request->getParam(’userEmail’);
$verbose
= $request->getParam(’verbose’);

26
27
28
29

// reset new password
$newPassword = Rand::getString(16);

30
31
32

// Fetch the user and change his password, then email him ...
// [...]

33
34
35

if (!$verbose) {
return "Done! $userEmail has received an email with his new password.n";
}else{
return "Done! New password for user $userEmail is ’$newPassword’. It has also been emaile
}

36
37
38
39
40

}

41
42

}

We have created resetpasswordAction() than retrieves current request and checks if it’s really coming from
the console (as a precaution). In this example we do not want our action to be invocable from a web page. Because
we have not defined any http route pointing to it, it should never be possible. However in the future, we might define
a wildcard route or a 3rd party module might erroneously route some requests to our action - that is why we want to
make sure that the request is always coming from a Console environment.
All console arguments supplied by the user are accessible via $request->getParam() method. Flags will be
represented by a booleans, where true means a flag has been used and false otherwise.

54.2. Handling console requests

239
Zend Framework 2 Documentation, Release 2.2.5

When our action has finished working it returns a simple string that will be shown to the user in console window.
See Also:
There are different ways you can interact with console from a controller. It has been covered in more detail in the
following chapter: Console-aware action controllers

54.3 Adding console usage info
It is a common practice for console application to display usage information when run for the first time (without any
arguments). This is also handled by ZendConsole together with MVC.
Usage info in ZF2 console applications is provided by loaded modules. In case no console route matches console
arguments, ZendConsole will query all loaded modules and ask for their console usage info.
Let’s modify our ApplicationModule to provide usage info:
1

<?php

2
3

namespace Application;

4
5
6
7
8

use
use
use
use

ZendModuleManagerFeatureAutoloaderProviderInterface;
ZendModuleManagerFeatureConfigProviderInterface;
ZendModuleManagerFeatureConsoleUsageProviderInterface;
ZendConsoleAdapterAdapterInterface as Console;

9
10
11
12
13
14
15
16
17
18

class Module implements
AutoloaderProviderInterface,
ConfigProviderInterface,
ConsoleUsageProviderInterface
{
public function getConfig()
{
// [...]
}

// <- our module implement this feature and provides console usag

19

public function getAutoloaderConfig()
{
// [...]
}

20
21
22
23
24

public function getConsoleUsage(Console $console)
{
return array(
// Describe available commands
’user resetpassword [--verbose|-v] EMAIL’

25
26
27
28
29

=> ’Reset password for a user’,

30

// Describe expected parameters
array( ’EMAIL’,
’Email of the user for a password reset’ ),
array( ’--verbose|-v’,
’(optional) turn on verbose mode’
),

31
32
33

);

34

}

35
36

}

Each module that implements ConsoleUsageProviderInterface will be queried for console usage info. On
route mismatch, all info from all modules will be concatenated, formatted to console width and shown to the user.

240

Chapter 54. Introduction to ZendConsole
Zend Framework 2 Documentation, Release 2.2.5

Note: The order of usage info displayed in the console is the order modules load. If you want your application to
display important usage info first, change the order your modules are loaded.
See Also:
Modules can also provide an application banner (title). To learn more about the format expected from
getConsoleUsage() and about application banners, please read this chapter: Console-aware modules

54.3. Adding console usage info

241
Zend Framework 2 Documentation, Release 2.2.5

242

Chapter 54. Introduction to ZendConsole
CHAPTER 55

Console routes and routing

Zend Framework 2 has native MVC integration with console, which means that command line arguments are read
and used to determine the appropriate action controller and action method that will handle the request. Actions can
perform any number of task prior to returning a result, that will be displayed to the user in his console window.
There are several routes you can use with Console. All of them are defined in ZendMvcRouterConsole*
classes.
See Also:
Routes are used to handle real commands, but they are not used to create help messages (usage information). When a
zf2 application is run in console for the first time (without arguments) it can display usage information that is provided
by modules. To learn more about providing usage information, please read this chapter: Console-aware modules.

55.1 Router configuration
All Console Routes are automatically read from the following configuration location:
1
2
3
4
5
6
7

// This can sit inside of modules/Application/config/module.config.php or any other module’s config.
array(
’router’ => array(
’routes’ => array(
// HTTP routes are here
)
),

8

’console’ => array(
’router’ => array(
’routes’ => array(
// Console routes go here
)
)
),

9
10
11
12
13
14
15
16

)

Console Routes will only be processed when the application is run inside console (terminal) window. They have no
effect in web (http) request and will be ignored. It is possible to define only HTTP routes (only web application) or
only Console routes (which means we want a console-only application which will refuse to run in a browser).
A single route can be described with the following array:
243
Zend Framework 2 Documentation, Release 2.2.5

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

// inside config.console.router.routes:
// [...]
’my-first-route’ => array(
’type’
=> ’simple’,
// <- simple route is created by default, we can skip that
’options’ => array(
’route’
=> ’foo bar’,
’defaults’ => array(
’controller’ => ’ApplicationControllerIndex’,
’action’
=> ’password’
)
)
)

We have created a simple console route with a name my-first-route. It expects two parameters: foo and bar.
If user puts these in a console, ApplicationControllerIndexController::passwordAction()
action will be invoked.
See Also:
You can read more about how ZF2 routing works in this chapter.

55.2 Basic route
This is the default route type for console. It recognizes the following types of parameters:
• Literal parameters (i.e. create object (external|internal))
• Literal flags (i.e. --verbose --direct [-d] [-a])
• Positional value parameters (i.e. create <modelName> [<destination>])
• Value flags (i.e. --name=NAME [--method=METHOD])

55.2.1 Literal parameters
These parameters are expected to appear on the command line exactly the way they are spelled in the route. For
example:
1
2
3
4
5
6
7
8
9

’show-users’ => array(
’options’ => array(
’route’
=> ’show users’,
’defaults’ => array(
’controller’ => ’ApplicationControllerUsers’,
’action’
=> ’show’
)
)
)

This route will only match for the following command line
> zf show users

It expects mandatory literal parameters show users. It will not match if there are any more params, or if one of
the words is missing. The order of words is also enforced.
We can also provide optional literal parameters, for example:

244

Chapter 55. Console routes and routing
Zend Framework 2 Documentation, Release 2.2.5

1
2
3
4
5
6
7
8
9

’show-users’ => array(
’options’ => array(
’route’
=> ’show [all] users’,
’defaults’ => array(
’controller’ => ’ApplicationControllerUsers’,
’action’
=> ’show’
)
)
)

Now this route will match for both of these commands:
> zf show users
> zf show all users

We can also provide parameter alternative:
1
2
3
4
5
6
7
8
9

’show-users’ => array(
’options’ => array(
’route’
=> ’show [all|deleted|locked|admin] users’,
’defaults’ => array(
’controller’ => ’ApplicationControllerUsers’,
’action’
=> ’show’
)
)
)

This route will match both without and with second parameter being one of the words, which enables us to capture
commands such:
> zf show users
> zf show locked users
> zf show admin users
etc.

Note: Whitespaces in route definition are ignored. If you separate your parameters with more spaces, or separate
alternatives and pipe characters with spaces, it won’t matter for the parser. The above route definition is equivalent to:
show [ all | deleted | locked | admin ] users

55.2.2 Literal flags
Flags are a common concept for console tools. You can define any number of optional and mandatory flags. The order
of flags is ignored. The can be defined in any order and the user can provide them in any other order.
Let’s create a route with optional long flags
1
2
3
4
5
6
7
8
9

’check-users’ => array(
’options’ => array(
’route’
=> ’check users [--verbose] [--fast] [--thorough]’,
’defaults’ => array(
’controller’ => ’ApplicationControllerUsers’,
’action’
=> ’check’
)
)
)

This route will match for commands like:
55.2. Basic route

245
Zend Framework 2 Documentation, Release 2.2.5

>
>
>
>

zf
zf
zf
zf

check
check
check
check

users
users --fast
users --verbose --thorough
users --thorough --fast

We can also define one or more mandatory long flags and group them as an alternative:
1
2
3
4
5
6
7
8
9

’check-users’ => array(
’options’ => array(
’route’
=> ’check users (--suspicious|--expired) [--verbose] [--fast] [--thorough]’,
’defaults’ => array(
’controller’ => ’ApplicationControllerUsers’,
’action’
=> ’check’
)
)
)

This route will only match if we provide either --suspicious or --expired flag, i.e.:
> zf check users --expired
> zf check users --expired --fast
> zf check users --verbose --thorough --suspicious

We can also use short flags in our routes and group them with long flags for convenience, for example:
1
2
3
4
5
6
7
8
9

’check-users’ => array(
’options’ => array(
’route’
=> ’check users [--verbose|-v] [--fast|-f] [--thorough|-t]’,
’defaults’ => array(
’controller’ => ’ApplicationControllerUsers’,
’action’
=> ’check’
)
)
)

Now we can use short versions of our flags:
> zf check users -f
> zf check users -v --thorough
> zf check users -t -f -v

55.2.3 Positional value parameters
Value parameters capture any text-based input and come in two forms - positional and flags.
Positional value parameters are expected to appear in an exact position on the command line. Let’s take a look at
the following route definition:
1
2
3
4
5
6
7
8
9

’delete-user’ => array(
’options’ => array(
’route’
=> ’delete user <userEmail>’,
’defaults’ => array(
’controller’ => ’ApplicationControllerUsers’,
’action’
=> ’delete’
)
)
)

This route will match for commands like:
246

Chapter 55. Console routes and routing
Zend Framework 2 Documentation, Release 2.2.5

> zf delete user john@acme.org
> zf delete user betty@acme.org

We can access the email value by calling $this->getRequest()->getParam(’userEmail’) inside of our
controller action (you can read more about accessing values here)
We can also define optional positional value parameters by adding square brackets:
1
2
3
4
5
6
7
8
9

’delete-user’ => array(
’options’ => array(
’route’
=> ’delete user [<userEmail>]’,
’defaults’ => array(
’controller’ => ’ApplicationControllerUsers’,
’action’
=> ’delete’
)
)
)

In this case, userEmail parameter will not be required for the route to match. If it is not provided, userEmail
parameter will not be set.
We can define any number of positional value parameters, for example:
1
2
3
4
5
6
7
8
9

’create-user’ => array(
’options’ => array(
’route’
=> ’create user <firstName> <lastName> <email> <position>’,
’defaults’ => array(
’controller’ => ’ApplicationControllerUsers’,
’action’
=> ’create’
)
)
)

This allows us to capture commands such as:
> zf create user Johnny Bravo john@acme.org Entertainer

Note: Command line arguments on all systems must be properly escaped, otherwise they will not be passed to our
application correctly. For example, to create a user with two names and a complex position description, we could write
something like this:
> zf create user "Johnan Tom" Bravo john@acme.org "Head of the Entertainment Department"

55.2.4 Value flag parameters
Positional value parameters are only matched if they appear in the exact order as described in the route. If we do not
want to enforce the order of parameters, we can define value flags.
Value flags can be defined and matched in any order. They can digest text-based values, for example:
1
2
3
4
5
6
7

’find-user’ => array(
’options’ => array(
’route’
=> ’find user [--id=] [--firstName=] [--lastName=] [--email=] [--position=] ’,
’defaults’ => array(
’controller’ => ’ApplicationControllerUsers’,
’action’
=> ’find’
)

55.2. Basic route

247
Zend Framework 2 Documentation, Release 2.2.5

)

8
9

)

This route will match for any of the following routes:
>
>
>
>
>
>
>

zf
zf
zf
zf
zf
zf
zf

find
find
find
find
find
find
find

user
user
user
user
user
user
user

--id 29110
--id=29110
--firstName=Johny --lastName=Bravo
--lastName Bravo --firstName Johny
--position=Executive --firstName=Bob
--position "Head of the Entertainment Department"

Note: The order of flags is irrelevant for the parser.

Note: The parser understands values that are provided after equal symbol (=) and separated by a space. Values
without whitespaces can be provided after = symbol or after a space. Values with one more whitespaces however,
must be properly quoted and written after a space.
In previous example, all value flags are optional. It is also possible to define mandatory value flags:
1
2
3
4
5
6
7
8
9

’rename-user’ => array(
’options’ => array(
’route’
=> ’rename user --id= [--firstName=] [--lastName=]’,
’defaults’ => array(
’controller’ => ’ApplicationControllerUsers’,
’action’
=> ’rename’
)
)
)

The --id parameter is required for this route to match. The following commands will work with this route:
> zf rename user --id 123
> zf rename user --id 123 --firstName Jonathan
> zf rename user --id=123 --lastName=Bravo

55.3 Catchall route
This special route will catch all console requests, regardless of the parameters provided.
1
2
3
4
5
6
7
8
9

’default-route’ => array(
’options’ => array(
’type’
=> ’catchall’,
’defaults’ => array(
’controller’ => ’ApplicationControllerIndex’,
’action’
=> ’consoledefault’
)
)
)

Note: This route type is rarely used. You could use it as a last console route, to display usage information. Before
you do so, read about the preferred way of displaying console usage information. It is the recommended way and will
guarantee proper inter-operation with other modules in your application.
248

Chapter 55. Console routes and routing
Zend Framework 2 Documentation, Release 2.2.5

55.4 Console routes cheat-sheet
Param type
Literal params
Literal
Literal alternative
Literal, optional
Literal, optional
alternative
Flags
Flag long
Flag long, optional

Example route definition

Explanation

foo
foo
foo
foo

“foo” followed by “bar”
“foo” followed by “bar” or “baz”
“foo”, optional “bar”
“foo”, optional “bar” or “baz”

bar
(bar|baz)
[bar]
[bar|baz]

foo --bar
foo [--bar]

Flag long, optional,
alternative
Flag short
Flag short, optional
Flag short, optional,
alternative
Flag long/short
alternative
Value parameters
Value positional param
Value positional param,
optional
Value Flag

foo [--bar|--baz]

Value Flag, optional

foo [--bar=]

Parameter groups
Literal params group
Literal optional params
group
Long flags group
Long optional flags
group
Short flags group
Short optional flags
group

foo -b
foo [-b]
foo [-b|-z]
foo [--bar|-b]

foo <bar>
foo [<bar>]
foo --bar=

“foo” as first parameter, “–bar” flag before or after
“foo” as first parameter, optional “–bar” flag before or
after
“foo” as first parameter, optional “–bar” or “–baz”,
before or after
“foo” as first parameter, “-b” flag before or after
“foo” as first parameter, optional “-b” flag before or after
“foo” as first parameter, optional “-b” or “-z”, before or
after
“foo” as first parameter, optional “–bar” or “-b” before
or after
“foo” followed by any text (stored as “bar” param)
“foo”, optionally followed by any text (stored as “bar”
param)
“foo” as first parameter, “–bar” with a value, before or
after
“foo” as first parameter, optionally “–bar” with a value,
before or after

foo
“foo” followed by “bar” or “baz” (stored as “myParam”
(bar|baz):myParam
param)
foo
“foo” followed by optional “bar” or “baz” (stored as
[bar|baz]:myParam
“myParam” param)
foo
“foo”, “bar” or “baz” flag before or after (stored as
(--bar|--baz):myParam
“myParam” param)
foo
“foo”, optional “bar” or “baz” flag before or after (as
[--bar|--baz]:myParam
“myParam” param)
foo
“foo”, “-b” or “-z” flag before or after (stored as
(-b|-z):myParam
“myParam” param)
foo
“foo”, optional “-b” or “-z” flag before or after (stored
[-b|-z]:myParam
as “myParam” param)

55.4. Console routes cheat-sheet

249
Zend Framework 2 Documentation, Release 2.2.5

250

Chapter 55. Console routes and routing
CHAPTER 56

Console-aware modules

Zend Framework 2 has native MVC integration with console. The integration also works with modules loaded with
Module Manager.
ZF2 ships with RouteNotFoundStrategy which is responsible of displaying usage information inside Console,
in case the user has not provided any arguments, or arguments could not be understood. The strategy currently supports
two types of information: application banners and usage information.

56.1 Application banner
To run the console ZF 2 component, go to your public folder, and type php index.php. By default, it will simply output
the current ZF 2 version, like this:

Our Application module (and any other module) can provide application banner. In order to do so, our Module class
has to implement ZendModuleManagerFeatureConsoleBannerProviderInterface. Let’s do this
now.

251
Zend Framework 2 Documentation, Release 2.2.5

1
2
3

// modules/Application/Module.php
<?php
namespace Application;

4
5
6

use ZendModuleManagerFeatureConsoleBannerProviderInterface;
use ZendConsoleAdapterAdapterInterface as Console;

7
8
9
10
11
12
13
14
15
16
17

class Module implements ConsoleBannerProviderInterface
{
/**
* This method is defined in ConsoleBannerProviderInterface
*/
public function getConsoleBanner(Console $console)
{
return ’MyModule 0.0.1’;
}
}

As you can see, the application banner should be a single line string that returns the module’s name and (if available)
its current version.
If several modules define their own banner, they are all shown one after the other (they will be joined together in the
order modules are loaded). This way, it makes it very easy to spot which modules provide console commands.
After running our application, we’ll see our newly created banner.

Let’s create and load second module that provides a banner.
1
2
3
4
5
6
7

<?php
// config/application.config.php
return array(
’modules’ => array(
’Application’,
’User’,
// < load user module in modules/User
),

User module will add-on a short info about itself:

252

Chapter 56. Console-aware modules
Zend Framework 2 Documentation, Release 2.2.5

1
2
3

// modules/User/Module.php
<?php
namespace User;

4
5
6

use ZendModuleManagerFeatureConsoleBannerProviderInterface;
use ZendConsoleAdapterAdapterInterface as Console;

7
8
9
10
11
12
13
14
15
16

class Module implements ConsoleBannerProviderInterface
{
/**
* This method is defined in ConsoleBannerProviderInterface
*/
public function getConsoleBanner(Console $console){
return "User Module 0.0.1";
}
}

Because User module is loaded after Application module, the result will look like this:

Note: Application banner is displayed as-is - no trimming or other adjustments will be performed on the text. As you
can see, banners are also automatically colorized as blue.

56.2 Usage information
In
order
to
display
usage
information,
our
Module
class
has
to
implement
ZendModuleManagerFeatureConsoleUsageProviderInterface.
Let’s modify our example
and add new method:
1
2
3

// modules/Application/Module.php
<?php
namespace Application;

4
5

use ZendModuleManagerFeatureConsoleBannerProviderInterface;

56.2. Usage information

253
Zend Framework 2 Documentation, Release 2.2.5

6
7

use ZendModuleManagerFeatureConsoleUsageProviderInterface;
use ZendConsoleAdapterAdapterInterface as Console;

8
9
10
11

class Module implements ConsoleBannerProviderInterface, ConsoleUsageProviderInterface
{
public function getConsoleBanner(Console $console){ // ... }

12

/**
* This method is defined in ConsoleUsageProviderInterface
*/
public function getConsoleUsage(Console $console)
{
return array(
’show stats’
=> ’Show application statistics’,
’run cron’
=> ’Run automated jobs’,
’(enable|disable) debug’ => ’Enable or disable debug mode for the application.’
);
}

13
14
15
16
17
18
19
20
21
22
23
24

}

This will display the following information:

Similar to application banner multiple modules can provide usage information, which will be joined together and
displayed to the user. The order in which usage information is displayed is the order in which modules are loaded.
As you can see, Console component also prepended each module’s usage by the module’s name. This helps to visually

254

Chapter 56. Console-aware modules
Zend Framework 2 Documentation, Release 2.2.5

separate each modules (this can be useful when you have multiple modules that provide commands). By default, the
component colorizes those in red.
Note: Usage info provided in modules does not connect with console routing. You can describe console usage in any
form you prefer and it does not affect how MVC handles console commands. In order to handle real console requests
you need to define 1 or more console routes.

56.2.1 Free-form text
In order to output free-form text as usage information, getConsoleUsage() can return a string, or an array of
strings, for example:
1
2
3
4

public function getConsoleUsage(Console $console)
{
return ’User module expects exactly one argument - user name. It will display information for thi
}

Note: The text provided is displayed as-is - no trimming or other adjustments will be performed. If you’d like to fit
your usage information inside console window, you could check its width with $console->getWidth().

56.2. Usage information

255
Zend Framework 2 Documentation, Release 2.2.5

56.2.2 List of commands
If getConsoleUsage() returns and associative array, it will be automatically aligned in 2 columns. The first
column will be prepended with script name (the entry point for the application). This is useful to display different
ways of running the application.
1
2
3
4
5
6
7
8
9

public function getConsoleUsage(Console $console)
{
return array(
’delete user <userEmail>’
=> ’Delete user with email <userEmail>’,
’disable user <userEmail>’
=> ’Disable user with email <userEmail>’,
’list [all|disabled] users’
=> ’Show a list of users’,
’find user [--email=] [--name=]’ => ’Attempt to find a user by email or name’,
);
}

Note: Commands and their descriptions will be aligned in two columns, that fit inside Console window. If the window
is resized, some texts might be wrapped but all content will be aligned accordingly. If you don’t like this behavior,
you can always return free-form text that will not be transformed in any way.

56.2.3 List of params and flags
Returning an array of arrays from getConsoleUsage() will produce a listing of parameters. This is useful for
explaining flags, switches, possible values and other information. The output will be aligned in multiple columns for
readability.
Below is an example:

256

Chapter 56. Console-aware modules
Zend Framework 2 Documentation, Release 2.2.5

1
2
3
4
5
6
7
8
9
10

public function getConsoleUsage(Console $console)
{
return array(
array( ’<userEmail>’
, ’email of the user’ ),
array( ’--verbose’
, ’Turn on verbose mode’ ),
array( ’--quick’
, ’Perform a "quick" operation’ ),
array( ’-v’
, ’Same as --verbose’ ),
array( ’-w’
, ’Wide output’)
);
}

Using this method, we can display more than 2 columns of information, for example:
1
2
3
4
5
6
7
8
9
10

public function getConsoleUsage(Console $console)
{
return array(
array( ’<userEmail>’ , ’user email’
array( ’--verbose’
, ’verbose mode’
array( ’--quick’
, ’"quick" operation’
array( ’-v’
, ’Same as --verbose’
array( ’-w’
, ’wide output’
);
}

56.2. Usage information

,
,
,
,
,

’Full email address of the user to find.’ ),
’Display additional information during processin
’Do not check integrity, just make changes and f
’Display additional information during processin
’When listing users, use the whole available scr

257
Zend Framework 2 Documentation, Release 2.2.5

Note: All info will be aligned in one or more columns that fit inside Console window. If the window is resized, some
texts might be wrapped but all content will be aligned accordingly. In case the number of columns changes (i.e. the
array() contains different number of elements) a new table will be started, with new alignment and different column
widths.
If you don’t like this behavior, you can always return free-form text that will not be transformed in any way.

56.2.4 Mixing styles
You can use mix together all of the above styles to provide comprehensive usage information, for example:
1
2
3
4
5
6

public function getConsoleUsage(Console $console)
{
return array(
’Finding and listing users’,
’list [all|disabled] users [-w]’
=> ’Show a list of users’,
’find user [--email=] [--name=]’
=> ’Attempt to find a user by email or name’,

7

array(’[all|disabled]’,
array(’--email=EMAIL’,
array(’--name=NAME’,
array(’-w’,

8
9
10
11

’Display all users or only disabled accounts’),
’Email of the user to find’),
’Full name of the user to find.’),
’Wide output - When listing users use the whole available screen w

12

’Manipulation of user database:’,
’delete user <userEmail> [--verbose|-v] [--quick]’
’disable user <userEmail> [--verbose|-v]’

13
14
15

=> ’Delete user with email <userEmail>’,
=> ’Disable user with email <userEmail>’,

16

array( ’<userEmail>’ , ’user email’
array( ’--verbose’
, ’verbose mode’

17
18

258

, ’Full email address of the user to change.’ ),
, ’Display additional information during processin

Chapter 56. Console-aware modules
Zend Framework 2 Documentation, Release 2.2.5

array( ’--quick’
array( ’-v’

19
20

, ’"quick" operation’ , ’Do not check integrity, just make changes and f
, ’Same as --verbose’ , ’Display additional information during processin

21

);

22
23

}

56.3 Best practices
As a reminder, here are the best practices when providing usage for your commands:
1. Your getConsoleBanner should only return a one-line string containing the module’s name and its version
(if available).
2. Your getConsoleUsage should not return module’s name; it is prepended automatically for you by Console
component.

56.3. Best practices

259
Zend Framework 2 Documentation, Release 2.2.5

260

Chapter 56. Console-aware modules
CHAPTER 57

Console-aware action controllers

Zend Framework 2 has built-in MVC integration with the console. When the user runs an application in a console
window, the request will be routed. By matching command line arguments against console routes we have defined in
our application, the MVC will invoke a controller and an action.
In this chapter we will learn how ZF2 Controllers can interact with and return output to console window.
See Also:
In order for a controller to be invoked, at least one route must point to it. To learn about creating console routes, please
read the chapter Console routes and routing

57.1 Handling console requests
Console requests are very similar to HTTP requests. In fact, they implement a common interface and are created at the
same time in the MVC workflow. Console routes match against command line arguments and provide a defaults
array, which holds the controller and action keys. These correspond with controller aliases in the ServiceManager, and method names in the controller class. This is analogous to the way HTTP requests are handled in
ZF2.
See Also:
To learn about defining and creating controllers, please read the chapter Routing and controllers
In this example we’ll use the following simple route:
1
2
3
4
5
6
7

// FILE: modules/Application/config/module.config.php
array(
’router’ => array(
’routes’ => array(
// HTTP routes are here
)
),

8
9
10
11
12
13
14

’console’ => array(
’router’ => array(
’routes’ => array(
’list-users’ => array(
’options’ => array(
’route’
=> ’show [all|disabled|deleted]:mode users [--verbose|-v]’,

261
Zend Framework 2 Documentation, Release 2.2.5

’defaults’ => array(
’controller’ => ’ApplicationControllerIndex’,
’action’
=> ’show-users’
)

15
16
17
18

)

19

)

20

)

21

)

22

),

23
24

)

This route will match commands such as:
> php public/index.php show users
> php public/index.php show all users
> php public/index.php show disabled users

This route points to the method ApplicationControllerIndexController::showUsersAction().
Let’s add it to our controller.
1
2

<?php
namespace ApplicationController;

3
4
5

use ZendMvcControllerAbstractActionController;
use ZendViewModelViewModel;

6
7
8
9
10
11
12

class IndexController extends AbstractActionController
{
public function indexAction()
{
return new ViewModel(); // display standard index page
}

13

public function showUsersAction()
{
$request = $this->getRequest();

14
15
16
17

// Check verbose flag
$verbose = $request->getParam(’verbose’) || $request->getParam(’v’);

18
19
20

// Check mode
$mode = $request->getParam(’mode’, ’all’); // defaults to ’all’

21
22
23

$users = array();
switch ($mode) {
case ’disabled’:
$users = $this->getServiceLocator()->get(’users’)->fetchDisabledUsers();
break;
case ’deleted’:
$users = $this->getServiceLocator()->get(’users’)->fetchDeletedUsers();
break;
case ’all’:
default:
$users = $this->getServiceLocator()->get(’users’)->fetchAllUsers();
break;
}

24
25
26
27
28
29
30
31
32
33
34
35
36

}

37
38

}

262

Chapter 57. Console-aware action controllers
Zend Framework 2 Documentation, Release 2.2.5

We fetch the console request, read parameters, and load users from our (theoretical) users service. In order to make
this method functional, we’ll have to display the result in the console window.

57.2 Sending output to console
The simplest way for our controller to display data in the console window is to return a string. Let’s modify our
example to output a list of users:
1
2
3

public function showUsersAction()
{
$request = $this->getRequest();

4

// Check verbose flag
$verbose = $request->getParam(’verbose’) || $request->getParam(’v’);

5
6
7

// Check mode
$mode = $request->getParam(’mode’, ’all’); // defaults to ’all’

8
9
10

$users = array();
switch ($mode) {
case ’disabled’:
$users = $this->getServiceLocator()->get(’users’)->fetchDisabledUsers();
break;
case ’deleted’:
$users = $this->getServiceLocator()->get(’users’)->fetchDeletedUsers();
break;
case ’all’:
default:
$users = $this->getServiceLocator()->get(’users’)->fetchAllUsers();
break;
}

11
12
13
14
15
16
17
18
19
20
21
22
23
24

if (count($users) == 0) {
// Show an error message in the console
return "There are no users in the databasen";
}

25
26
27
28
29

$result = ’’;

30
31

foreach ($users as $user) {
$result .= $user->name . ’ ’ . $user->email . "n";
}

32
33
34
35

return $result; // show it in the console

36
37

}

On line 27, we are checking if the users service found any users - otherwise we are returning an error message that
will be immediately displayed and the application will end.
If there are 1 or more users, we will loop through them with and prepare a listing. It is then returned from the action
and displayed in the console window.

57.2. Sending output to console

263
Zend Framework 2 Documentation, Release 2.2.5

57.3 Are we in a console?
Sometimes we might need to check if our method is being called from a console or from a web request. This is useful
to block certain methods from running in the console or to change their behavior based on that context.
Here is an example of how to check if we are dealing with a console request:
1

namespace ApplicationController;

2
3
4
5
6

use
use
use
use

ZendMvcControllerAbstractActionController;
ZendViewModelViewModel;
ZendConsoleRequest as ConsoleRequest;
RuntimeException;

7
8
9
10
11
12

class IndexController extends AbstractActionController
{
public function showUsersAction()
{
$request = $this->getRequest();

13

// Make sure that we are running in a console and the user has not tricked our
// application into running this action from a public web server.
if (!$request instanceof ConsoleRequest) {
throw new RuntimeException(’You can only use this action from a console!’);
}
// ...

14
15
16
17
18
19

}

20
21

}

Note: You do not need to secure all your controllers and methods from console requests. Controller actions will
only be invoked when at least one console route matches it. HTTP and Console routes are separated and defined in
different places in module (and application) configuration.
There is no way to invoke a console action unless there is at least one route pointing to it. Similarly, there is no way
for an HTTP action to be invoked unless there is at least one HTTP route that points to it.
The example below shows how a single controller method can handle both Console and HTTP requests:
1

namespace ApplicationController;

2
3
4
5
6
7

use
use
use
use
use

ZendMvcControllerAbstractActionController;
ZendViewModelViewModel;
ZendConsoleRequest as ConsoleRequest;
ZendHttpRequest as HttpRequest;
RuntimeException;

8
9
10
11
12
13

class IndexController extends AbstractActionController
{
public function showUsersAction()
{
$request = $this->getRequest();

14

$users = array();
// ... fetch users from database ...

15
16
17

if ($request instanceof HttpRequest) {
// display a web page with users list

18
19

264

Chapter 57. Console-aware action controllers
Zend Framework 2 Documentation, Release 2.2.5

return new ViewModel($result);
} elseif ($request instanceof ConsoleRequest) {
// ... prepare console output and return it ...
return $result;
} else {
throw new RuntimeException(’Cannot handle request of type ’ . get_class($request));
}

20
21
22
23
24
25
26

}

27
28

}

57.4 Reading values from console parameters
There are several types of parameters recognized by the Console component - all of them are described in the console
routing chapter. Here, we’ll focus on how to retrieve values from distinct parameters and flags.

57.4.1 Positional parameters
After a route matches, we can access both literal parameters and value parameters from within the $request
container.
Assuming we have the following route:
1
2
3
4
5
6
7
8
9
10

// inside of config.console.router.routes:
’show-users’ => array(
’options’ => array(
’route’
=> ’show (all|deleted|locked|admin) [<groupName>]’
’defaults’ => array(
’controller’ => ’ApplicationControllerUsers’,
’action’
=> ’showusers’
)
)
)

If this route matches, our action can now query parameters in the following way:
1
2
3
4

// an action inside ApplicationControllerUsersController:
public function showUsersAction()
{
$request = $this->getRequest();

5

// We can access named value parameters directly by their name:
$showUsersFromGroup = $request->getParam(’groupName’);

6
7
8

// Literal parameters can be checked with isset() against their exact spelling
if (isset($request->getParam(’all’))) {
// show all users
} elseif (isset($request->getParam(’deleted’))) {
// show deleted users
}
// ...

9
10
11
12
13
14
15
16

}

In case of parameter alternatives, it is a good idea to assign a name to the group, which simplifies the branching in
our action controllers. We can do this with the following syntax:

57.4. Reading values from console parameters

265
Zend Framework 2 Documentation, Release 2.2.5

1
2
3
4
5
6
7
8
9
10

// inside of config.console.router.routes:
’show-users’ => array(
’options’ => array(
’route’
=> ’show (all|deleted|locked|admin):userTypeFilter [<groupName>]’
’defaults’ => array(
’controller’ => ’ApplicationControllerUsers’,
’action’
=> ’showusers’
)
)
)

Now we can use a the group name userTypeFilter to check which option has been selected by the user:
1
2
3

public function showUsersAction()
{
$request = $this->getRequest();

4

// We can access named value parameters directly by their name:
$showUsersFromGroup = $request->getParam(’groupName’);

5
6
7

// The selected option from second parameter is now stored under ’userTypeFilter’
$userTypeFilter
= $request->getParam(’userTypeFilter’);

8
9
10

switch ($userTypeFilter) {
case ’all’:
// all users
case ’deleted’:
// deleted users
case ’locked’
// ...
// ...
}

11
12
13
14
15
16
17
18
19
20

}

57.4.2 Flags
Flags are directly accessible by name. Value-capturing flags will contain string values, as provided by the user. Nonvalue flags will be equal to true.
Given the following route:
1
2
3
4
5
6
7
8
9

’find-user’ => array(
’options’ => array(
’route’
=> ’find user [--fast] [--verbose] [--id=] [--firstName=] [--lastName=] [--email=]
’defaults’ => array(
’controller’ => ’ApplicationControllerUsers’,
’action’
=> ’find’,
)
)
)

We can easily retrieve values in the following fashion:
1
2
3

public function findAction()
{
$request = $this->getRequest();

4

// We can retrieve values from value flags using their name

5

266

Chapter 57. Console-aware action controllers
Zend Framework 2 Documentation, Release 2.2.5

$searchId
$searchFirstName
$searchLastName
$searchEmail

6
7
8
9

=
=
=
=

$request->getParam(’id’,
$request->getParam(’firstName’,
$request->getParam(’lastName’,
$request->getParam(’email’,

null); // default null
null);
null);
null);

10

// Standard flags that have been matched will be equal to TRUE
$isFast
= (bool) $request->getParam(’fast’,
false); // default false
$isVerbose
= (bool) $request->getParam(’verbose’,false);

11
12
13
14

if ($isFast) {
// perform a fast query ...
} else {
// perform standard query ...
}

15
16
17
18
19
20

}

In case of flag alternatives, we have to check each alternative separately:
1
2
3
4
5
6

// Assuming our route now reads:
//
’route’
=> ’find user [--fast|-f] [--verbose|-v] ... ’,
//
public function findAction()
{
$request = $this->getRequest();

7

// Check both alternatives
$isFast
= $request->getParam(’fast’,false)
|| $request->getParam(’f’,false);
$isVerbose = $request->getParam(’verbose’,false) || $request->getParam(’v’,false);

8
9
10
11

// ...

12
13

}

57.4. Reading values from console parameters

267
Zend Framework 2 Documentation, Release 2.2.5

268

Chapter 57. Console-aware action controllers
CHAPTER 58

Console adapters

Zend Framework 2 provides console abstraction layer, which works around various bugs and limitations in operating
systems. It handles displaying of colored text, retrieving console window size, charset and provides basic line drawing
capabilities.
See Also:
Console Adapters can be used for a low-level access to the console. If you plan on building functional console
applications you do not normally need to use adapters. Make sure to read about console MVC integration first,
because it provides a convenient way for running modular console applications without directly writing to or reading
from console window.

58.1 Retrieving console adapter
If you are using MVC controllers you can obtain Console adapter instance using Service Manager.
1

namespace Application;

2
3
4
5

use ZendMvcControllerAbstractActionController;
use ZendConsoleAdapterAdapterInterface as Console;
use ZendConsoleExceptionRuntimeException;

6
7
8
9
10
11
12
13
14
15
16

class ConsoleController extends AbstractActionController
{
public function testAction()
{
$console = $this->getServiceLocator()->get(’console’);
if (!$console instanceof Console) {
throw new RuntimeException(’Cannot obtain console adapter. Are we running in a console?’)
}
}
}

If you are using ZendConsole without MVC, we can get adapter using the following code:
1
2

use ZendConsoleConsole;
use ZendConsoleExceptionRuntimeException as ConsoleException;

3
4

try {

269
Zend Framework 2 Documentation, Release 2.2.5

5
6
7
8

$console = Console::getInstance();
} catch (ConsoleException $e) {
// Could not get console adapter - most likely we are not running inside a console window.
}

Note: For practical and security reasons, Console::getInstance() will always throw an exception if you
attempt to get console instance in a non-console environment (i.e. when running on a HTTP server). You can override
this behavior by manually instantiating one of ZendConsoleAdapter* classes.

58.2 Using console adapter
58.2.1 Window size and title
$console->getWidth() (int) Get real console window width in characters.
$console->getHeight() (int) Get real console window height in characters.
$console->getSize() (array) Get an array( $width, $height) with current console window dimensions.
$console->getTitle() (string) Get console window title.
Note: For UTF-8 enabled consoles (terminals) dimensions represent the number of multibyte characters (real characters).

Note: On consoles with virtual buffers (i.e. MS Windows Command Prompt) width and height represent visible
(real) size, without scrolling the window. For example - if the window scrolling width is 120 chars, but it’s real, visible
width is 80 chars, getWidth() will return 80.

58.2.2 Character set
$console->isUtf8() (boolean) Is the console UTF-8 compatible (can display unicode strings) ?
$console->getCharset() (ZendConsoleCharsetCharsetInterface) This method will return one of
ConsoleCharset* classes that represent the readable charset that can be used for line-drawing. It
is automatically detected by the adapter.

58.2.3 Writing to console
$console->write( string $text, $color = null, $bgColor = null ) Write a $text to the console, optionally using foreground $color and background $bgColor.
Color value is one of the constants in
ZendConsoleColorInterface.
$console->writeLine( string $text, $color = null, $bgColor = null ) Write a single line of $text to the console.
This method will output a newline character at the end of text moving console cursor to next line.
$console->writeAt( string $text, int $x, int $y, $color = null, $bgColor = null ) Write $text at the specified $x
and $y coordinates of console window. Top left corner of the screen has coordinates of $x = 1; $x = 1.
To retrieve far-right and bottom coordinates, use getWidth() and getHeight() methods.

270

Chapter 58. Console adapters
Zend Framework 2 Documentation, Release 2.2.5

58.2.4 Reading from console
$console->readChar( string $mask = null ) (string) Read a single character from console. Optional (string)
$mask can be provided to force entering only a selected set of characters. For example, to read a single digit,
we can use the following syntax: $digit = $console->readChar(’0123456789’);
$console->readLine( int $maxLength = 2048 ) (string) Read a single line of input from console. Optional (int)
$maxLength can be used to limit the length of data that will be read. The line will be returned without ending
newline character.

58.2.5 Miscellaneous
$console->hideCursor() Hide blinking cursor from console.
$console->showCursor() Show blinking cursor in console.
$console->clear() Clear the screen.
$console->clearLine() Clear the line that the cursor currently sits at.

58.2. Using console adapter

271
Zend Framework 2 Documentation, Release 2.2.5

272

Chapter 58. Console adapters
CHAPTER 59

Console prompts

In addition to console abstraction layer Zend Framework 2 provides numerous convenience classes for interacting
with the user in console environment. This chapter describes available ZendConsolePrompt classes and their
example usage.
All prompts can be instantiated as objects and provide show() method.
1

use ZendConsolePrompt;

2
3
4
5
6
7

$confirm = new PromptConfirm(’Are you sure you want to continue?’);
$result = $confirm->show();
if ($result) {
// the user chose to continue
}

There is also a shorter method of displaying prompts, using static prompt() method:
1

use ZendConsolePrompt;

2
3
4
5
6

$result = PromptConfirm::prompt(’Are you sure you want to continue?’);
if ($result) {
// the user chose to continue
}

Both of above examples will display something like this:

See Also:
Make sure to read about console MVC integration first, because it provides a convenient way for running modular
console applications without directly writing to or reading from console window.

273
Zend Framework 2 Documentation, Release 2.2.5

59.1 Confirm
This prompt is best used for a yes / no type of choices.
Confirm( string $text, string $yesChar = ’y’, string $noChar = ’n’ )

$text (string) The text to show with the prompt
$yesChar (string) The char that corresponds with YES choice. Defaults to y.
$noChar (string) The char that corresponds with NO choice. Defaults to n.
Example usage:
use ZendConsolePromptConfirm;
if ( Confirm::prompt(’Is this the correct answer? [y/n]’, ’y’, ’n’) ) {
$console->write("You chose YES");
} else {
$console->write("You chose NO");
}

59.2 Line
This prompt asks for a line of text input.
Line(
string $text = ’Please enter value’,
bool $allowEmpty = false,
bool $maxLength = 2048
)

$text (string) The text to show with the prompt
$allowEmpty (boolean) Can this prompt be skipped, by pressing [ENTER] ? (default fo false)
$maxLength (integer) Maximum length of the input. Anything above this limit will be truncated.
Example usage:
use ZendConsolePromptLine;
$name = Line::prompt(
’What is your name?’,
false,
100

274

Chapter 59. Console prompts
Zend Framework 2 Documentation, Release 2.2.5

);
$console->write("Good day to you $name!");

59.3 Char
This prompt reads a single keystroke and optionally validates it against a list o allowed characters.
Char(
string
string
bool
bool
bool
)

$text = ’Please hit a key’,
$allowedChars = ’abc’,
$ignoreCase = true,
$allowEmpty = false,
$echo = true

$text (string) The text to show with the prompt
$allowedChars (string) A list of allowed keys that can be pressed.
$ignoreCase (boolean) Ignore the case of chars pressed (default to true)
$allowEmpty (boolean) Can this prompt be skipped, by pressing [ENTER] ? (default fo false)
$echo (boolean) Should the selection be displayed on the screen ?
Example usage:
use ZendConsolePromptChar;
$answer = Char::prompt(
’What is the correct answer? [a,b,c,d,e]’,
’abcde’,
true,
false,
true
);
if ($answer == ’b’) {
$console->write(’Correct. This it the right answer’);
} else {
$console->write(’Wrong ! Try again.’);
}

59.3. Char

275
Zend Framework 2 Documentation, Release 2.2.5

59.4 Select
This prompt displays a number of choices and asks the user to pick one.
Select(
string
array
bool
bool
)

$text = ’Please select one option’,
$options = array(),
$allowEmpty = false,
$echo = false

$text (string) The text to show with the prompt
$options (array) An associative array with keys strokes (chars) and their displayed values.
$allowEmpty (boolean) Can this prompt be skipped, by pressing [ENTER] ? (default fo false)
$echo (boolean) Should the selection be displayed on the screen ?
Example usage:
$options =
’a’ =>
’o’ =>
’p’ =>
’b’ =>
’n’ =>
);

array(
’Apples’,
’Oranges’,
’Pears’,
’Bananas’,
’none of the above...’

$answer = Select::prompt(
’Which fruit do you like the best?’,
$options,
false,
false
);
$console->write("You told me that you like " . $options[$answer]);

276

Chapter 59. Console prompts
Zend Framework 2 Documentation, Release 2.2.5

See Also:
To learn more about accessing console, writing to and reading from it, make sure to read the following chapter:
Console adapters.

59.4. Select

277
Zend Framework 2 Documentation, Release 2.2.5

278

Chapter 59. Console prompts
CHAPTER 60

Introduction to ZendCrypt

ZendCrypt provides support of some cryptographic tools. The available features are:
• encrypt-then-authenticate using symmetric ciphers (the authentication step is provided using HMAC);
• encrypt/decrypt using symmetric and public key algorithm (e.g. RSA algorithm);
• generate digital sign using public key algorithm (e.g. RSA algorithm);
• key exchange using the Diffie-Hellman method;
• Key derivation function (e.g. using PBKDF2 algorithm);
• Secure password hash (e.g. using Bcrypt algorithm);
• generate Hash values;
• generate HMAC values;
The main scope of this component is to offer an easy and secure way to protect and authenticate sensitive data in PHP.
Because the use of cryptography is not so easy we recommend to use the ZendCrypt component only if you have
a minimum background on this topic. For an introduction to cryptography we suggest the following references:
• Dan Boneh “Cryptography course” Stanford University, Coursera - free online course
• N.Ferguson, B.Schneier, and T.Kohno, “Cryptography Engineering”, John Wiley & Sons (2010)
• B.Schneier “Applied Cryptography”, John Wiley & Sons (1996)
Note: PHP-CryptLib
Most of the ideas behind the ZendCrypt component have been inspired by the PHP-CryptLib project of Anthony
Ferrara. PHP-CryptLib is an all-inclusive pure PHP cryptographic library for all cryptographic needs. It is meant to
be easy to install and use, yet extensible and powerful enough for even the most experienced developer.

279
Zend Framework 2 Documentation, Release 2.2.5

280

Chapter 60. Introduction to ZendCrypt
CHAPTER 61

Encrypt/decrypt using block ciphers

ZendCryptBlockCipher implements the encrypt-then-authenticate mode using HMAC to provide authentication.
The symmetric cipher can be choose with a specific adapter that implements the
ZendCryptSymmetricSymmetricInterface. We support the standard algorithms of the Mcrypt
extension. The adapter that implements the Mcrypt is ZendCryptSymmetricMcrypt.
In the following code we reported an example on how to use the BlockCipher class to encrypt-then-authenticate a string
using the AES block cipher (with a key of 256 bit) and the HMAC algorithm (using the SHA-256 hash function).
1

use ZendCryptBlockCipher;

2
3
4
5
6

$blockCipher = BlockCipher::factory(’mcrypt’, array(’algo’ => ’aes’));
$blockCipher->setKey(’encryption key’);
$result = $blockCipher->encrypt(’this is a secret message’);
echo "Encrypted text: $result n";

The BlockCipher is initialized using a factory method with the name of the cipher adapter to use (mcrypt) and the
parameters to pass to the adapter (the AES algorithm). In order to encrypt a string we need to specify an encryption
key and we used the setKey() method for that scope. The encryption is provided by the encrypt() method.
The output of the encryption is a string, encoded in Base64 (default), that contains the HMAC value, the IV vector, and
the encrypted text. The encryption mode used is the CBC (with a random IV by default) and SHA256 as default hash
algorithm of the HMAC. The Mcrypt adapter encrypts using the PKCS#7 padding mechanism by default. You can
specify a different padding method using a special adapter for that (ZendCryptSymmetricPadding). The encryption
and authentication keys used by the BlockCipher are generated with the PBKDF2 algorithm, used as key derivation
function from the user’s key specified using the setKey() method.
Note: Key size
BlockCipher try to use always the longest size of the key for the specified cipher. For instance, for the AES algorithm
it uses 256 bits and for the Blowfish algorithm it uses 448 bits.
You can change all the default settings passing the values to the factory parameters. For instance, if you want to use
the Blowfish algorithm, with the CFB mode and the SHA512 hash function for HMAC you have to initialize the class
as follow:
1

use ZendCryptBlockCipher;

2

281
Zend Framework 2 Documentation, Release 2.2.5

3
4
5
6
7

$blockCipher = BlockCipher::factory(’mcrypt’, array(
’algo’ => ’blowfish’,
’mode’ => ’cfb’,
’hash’ => ’sha512’
));

Note: Recommendation
If you are not familiar with symmetric encryption techniques we strongly suggest to use the default values of the
BlockCipher class. The default values are: AES algorithm, CBC mode, HMAC with SHA256, PKCS#7 padding.
To decrypt a string we can use the decrypt() method. In order to successfully decrypt a string we have to configure
the BlockCipher with the same parameters of the encryption.
We can also initialize the BlockCipher manually without use the factory method. We can inject the symmetric cipher
adapter directly to the constructor of the BlockCipher class. For instance, we can rewrite the previous example as
follow:
1
2

use ZendCryptBlockCipher;
use ZendCryptSymmetricMcrypt;

3
4
5
6
7

$blockCipher = new BlockCipher(new Mcrypt(array(’algo’ => ’aes’)));
$blockCipher->setKey(’encryption key’);
$result = $blockCipher->encrypt(’this is a secret message’);
echo "Encrypted text: $result n";

282

Chapter 61. Encrypt/decrypt using block ciphers
CHAPTER 62

Key derivation function

In cryptography, a key derivation function (or KDF) derives one or more secret keys from a secret value such as
a master key or other known information such as a password or passphrase using a pseudo-random function. For
instance, a KDF function can be used to generate encryption or authentication keys from a user password. The
ZendCryptKeyDerivation implements a key derivation function using specific adapters.
User passwords are not really suitable to be used as keys in cryptographic algorithms, since users normally choose
keys they can write on keyboard. These passwords use only 6 to 7 bits per character (or less). It is highly recommended
to use always a KDF function to transform a user’s password in a cryptographic key.
The output of the following key derivation functions is a binary string. If you need to store the value in a database
or a different persistent storage, we suggest to convert it in Base64 format, using base64_encode() function, or in hex
format, using the bin2hex() function.

62.1 Pbkdf2 adapter
Pbkdf2 is a KDF that applies a pseudorandom function, such as a cryptographic hash, to the input password or
passphrase along with a salt value and repeats the process many times to produce a derived key, which can then
be used as a cryptographic key in subsequent operations. The added computational work makes password cracking
much more difficult, and is known as key stretching.
In the example below we show a typical usage of the Pbkdf2 adapter.
1
2

use ZendCryptKeyDerivationPbkdf2;
use ZendMathRand;

3
4
5
6

$pass = ’password’;
$salt = Rand::getBytes(strlen($pass), true);
$key = Pbkdf2::calc(’sha256’, $pass, $salt, 10000, strlen($pass)*2);

7
8
9

printf ("Original password: %sn", $pass);
printf ("Derived key (hex): %sn", bin2hex($key));

The Pbkdf2 adapter takes the password ($pass) and generate a binary key with a size double of the password. The
syntax is calc($hash, $pass, $salt, $iterations, $length) where $hash is the name of the hash
function to use, $pass is the password, $salt is a pseudo random value, $iterations is the number of iterations
of the algorithm and $length is the size of the key to be generated. We used the Rand::getBytes function of

283
Zend Framework 2 Documentation, Release 2.2.5

the ZendMathRand class to generate a random bytes using a strong generators (the true value means the usage
of strong generators).
The number of iterations is a very important parameter for the security of the algorithm. Big values means more
security. There is not a fixed value for that because the number of iterations depends on the CPU power. You should
always choose a number of iteration that prevent brute force attacks. For instance, a value of 1‘000‘000 iterations, that
is equal to 1 sec of elaboration for the PBKDF2 algorithm, is enough secure using an Intel Core i5-2500 CPU at 3.3
Ghz.

62.2 SaltedS2k adapter
The SaltedS2k algorithm uses an hash function and a salt to generate a key based on a user’s password. This algorithm
doesn’t use a parameter that specify the number of iterations and for that reason it’s considered less secure compared
with Pbkdf2. We suggest to use the SaltedS2k algorithm only if you really need it.
Below is reported a usage example of the SaltedS2k adapter.
1
2

use ZendCryptKeyDerivationSaltedS2k;
use ZendMathRand;

3
4
5
6

$pass = ’password’;
$salt = Rand::getBytes(strlen($pass), true);
$key = SaltedS2k::calc(’sha256’, $pass, $salt, strlen($pass)*2);

7
8
9

printf ("Original password: %sn", $pass);
printf ("Derived key (hex): %sn", bin2hex($key));

62.3 Scrypt adapter
The scrypt algorithm uses the algorithm Salsa20/8 core and Pbkdf2-SHA256 to generate a key based on a user’s
password. This algorithm has been designed to be more secure against hardware brute-force attacks than alternative
functions such as Pbkdf2 or bcrypt.
The scrypt algorithm is based on the idea of memory-hard algorithms and sequential memory-hard functions. A
memory-hard algorithm is thus an algorithm which asymptotically uses almost as many memory locations as it uses
operations[#f1]_. A natural way to reduce the advantage provided by an attackers ability to construct highly parallel
circuits is to increase the size of a single key derivation circuit if a circuit is twice as large, only half as many copies can
be placed on a given area of silicon while still operating within the resources available to software implementations,
including a powerful CPU and large amounts of RAM.
“From a test executed on modern (2009) hardware, if 5 seconds are spent computing a derived key, the
cost of a hardware brute-force attack against scrypt is roughly 4000 times greater than the cost of a similar
attack against bcrypt (to find the same password), and 20000 times greater than a similar attack against
Pbkdf2.” Colin Percival (the author of scrypt algorithm)
This algorithm uses 4 parameters to generate a key of 64 bytes:
• salt, a random string;
• N, the CPU cost;
• r, the memory cost;
• p, the parallelization cost.
Below is reported a usage example of the Scrypt adapter.

284

Chapter 62. Key derivation function
Zend Framework 2 Documentation, Release 2.2.5

1
2

use ZendCryptKeyDerivationScrypt;
use ZendMathRand;

3
4
5
6

$pass = ’password’;
$salt = Rand::getBytes(strlen($pass), true);
$key = Scrypt::calc($pass, $salt, 2048, 2, 1, 64);

7
8
9

printf ("Original password: %sn", $pass);
printf ("Derived key (hex): %sn", bin2hex($key));

Note: Performance of the scrypt implementation
The aim of the scrypt algorithm is to generate secure derived key preventing brute force attacks. Just like the other
derivation functions, the more time (and memory) we spent executing the algorithm, the more secure the derived
key will be. Unfortunately a pure PHP implementation of the scrypt algorithm is very slow compared with the C
implementation (this is always true, if you compare execution time of C with PHP). If you want use a faster scrypt
algorithm we suggest to install the scrypt PECL extension. The Scrypt adapter of Zend Framework is able to recognize
if the PECL extension is loaded and use it instead of the pure PHP implementation.

62.3. Scrypt adapter

285
Zend Framework 2 Documentation, Release 2.2.5

286

Chapter 62. Key derivation function
CHAPTER 63

Password

In the ZendCryptPassword namespace you can find all the password formats supported by Zend Framework.
We currently support the following passwords:
• bcrypt;
• Apache (htpasswd).
If you need to choose a password format to store the user’s password we suggest to use the bcrypt algorithm that is
considered secure against brute forcing attacks (see the details below).

63.1 Bcrypt
The bcrypt algorithm is an hashing algorithm that is widely used and suggested by the security community to store
users passwords in a secure way.
Classic hashing mechanisms like MD5 or SHA, with or without a salt value, are not considered secure anymore (read
this post to know why).
The security of bcrypt is related to the speed of the algorithm. Bcrypt is very slow, it can request even a second to
generate an hash value. That means a brute force attack is impossible to execute, due to the amount of time that its
need.
Bcrypt uses a cost parameter that specify the number of cycles to use in the algorithm. Increasing this number the
algorithm will spend more time to generate the hash output. The cost parameter is represented by an integer value
between 4 to 31. The default cost value of the ZendCryptPasswordBcrypt component is 14, that means
almost a second using a CPU Intel i5 at 3.3Ghz (the cost parameter is a relative value according to the speed of the
CPU used).
If you want to change the cost parameter of the bcrypt algorithm you can use the setCost() method. Please note
that if you change the cost parameter, the resulting hash will be different. This will not affect the verification process
of the algorithm, therefore not breaking the password hashes you already have stored. Bcrypt reads the cost parameter
from the hash value, during the password authentication. All of the parts needed to verify the hash are all together,
separated with $’s, first the algorithm, then the cost, the salt, and then finally the hash.
The example below shows how to use the bcrypt algorithm to store a user’s password:
1

use ZendCryptPasswordBcrypt;

2

287
Zend Framework 2 Documentation, Release 2.2.5

3
4

$bcrypt = new Bcrypt();
$securePass = $bcrypt->create(’user password’);

The output of the create() method is the hash of the password. This value can then be stored in a repository like a
database (the output is a string of 60 bytes).
To verify if a given password is valid against a bcrypt value you can use the verify() method. An example is
reported below:
1

use ZendCryptPasswordBcrypt;

2
3
4
5

$bcrypt = new Bcrypt();
$securePass = ’the stored bcrypt value’;
$password = ’the password to check’;

6
7
8
9
10
11

if ($bcrypt->verify($password, $securePass)) {
echo "The password is correct! n";
} else {
echo "The password is NOT correct.n";
}

In the bcrypt uses also a salt value to improve the randomness of the algorithm.
By default, the
ZendCryptPasswordBcrypt component generates a random salt for each hash. If you want to specify a
preselected salt you can use the setSalt() method.
We provide also a getSalt() method to retrieve the salt specified by the user. The salt and the cost parameter can
be also specified during the constructor of the class, below is reported an example:
1

use ZendCryptPasswordBcrypt;

2
3
4
5
6

$bcrypt = new Bcrypt(array(
’salt’ => ’random value’,
’cost’ => 13
));

Note: Bcrypt with non-ASCII passwords (8-bit characters)
The bcrypt implementation used by PHP < 5.3.7 can contains a security flaw if the password uses 8-bit characters
(here’s the security report). The impact of this bug was that most (but not all) passwords containing non-ASCII characters with the 8th bit set were hashed incorrectly, resulting in password hashes incompatible with those of OpenBSD’s
original implementation of bcrypt. This security flaw has been fixed starting from PHP 5.3.7 and the prefix used in the
output was changed to ‘$2y$’ in order to put evidence on the correctness of the hash value. If you are using PHP <
5.3.7 with 8-bit passwords, the ZendCryptPasswordBcrypt throws an exception suggesting to upgrade to
PHP 5.3.7+ or use only 7-bit passwords.

63.2 Apache
The ZendCryptPasswordApache supports all the password formats used by Apache (htpasswd). These
formats are:
• CRYPT, uses the traditional Unix crypt(3) function with a randomly-generated 32-bit salt (only 12 bits used)
and the first 8 characters of the password;
• SHA1, “{SHA}” + Base64-encoded SHA-1 digest of the password;

288

Chapter 63. Password
Zend Framework 2 Documentation, Release 2.2.5

• MD5, “$apr1$” + the result of an Apache-specific algorithm using an iterated (1,000 times) MD5 digest of
various combinations of a random 32-bit salt and the password.
• Digest, the MD5 hash of the string user:realm:password as a 32-character string of hexadecimal digits. realm is
the Authorization Realm argument to the AuthName directive in httpd.conf.
In order to specify the format of the Apache’s password you can use the setFormat() method. An example with
all the formats usage is reported below:
1

use ZendCryptPasswordApache;

2
3

$apache = new Apache();

4
5
6

$apache->setFormat(’crypt’);
printf ("CRYPT output: %sn", $apache->create(’password’));

7
8
9

$apache->setFormat(’sha1’);
printf ("SHA1 output: %sn", $apache->create(’password’));

10
11
12

$apache->setFormat(’md5’);
printf ("MD5 output: %sn", $apache->create(’password’));

13
14
15
16
17

$apache->setFormat(’digest’);
$apache->setUserName(’enrico’);
$apache->setAuthName(’test’);
printf ("Digest output: %sn", $apache->create(’password’));

You can also specify the format of the password during the constructor of the class:
1

use ZendCryptPasswordApache;

2
3
4
5

$apache = new Apache(array(
’format’ => ’md5’
));

Other possible parameters to pass in the constructor are username and authname, for the digest format.

63.2. Apache

289
Zend Framework 2 Documentation, Release 2.2.5

290

Chapter 63. Password
CHAPTER 64

Public key cryptography

Public-key cryptography refers to a cryptographic system requiring two separate keys, one of which is secret and one
of which is public. Although different, the two parts of the key pair are mathematically linked. One key locks or
encrypts the plaintext, and the other unlocks or decrypts the cyphertext. Neither key can perform both functions. One
of these keys is published or public, while the other is kept private.
In Zend Framework we implemented two public key algorithms: Diffie-Hellman key exchange and RSA.

64.1 Diffie-Hellman
The Diffie-Hellman algorithm is a specific method of exchanging cryptographic keys. It is one of the earliest practical
examples of key exchange implemented within the field of cryptography. The DiffieHellman key exchange method
allows two parties that have no prior knowledge of each other to jointly establish a shared secret key over an insecure
communications channel. This key can then be used to encrypt subsequent communications using a symmetric key
cipher.
The diagram of operation of the Diffie-Hellman algorithm can be defined by the following picture (taken by the DiffieHellman Wikipedia page):
The schema’s colors represent the parameters of the algorithm. Here is reported an example of usage using the
ZendCryptPublicKeyDiffieHellman class:
1

use ZendCryptPublicKeyDiffieHellman;

2
3
4
5
6
7
8
9
10
11
12
13

$aliceOptions = array(
’prime’
=> ’1551728981814736974712322577637155399157248019669154044797077953140576293785419175
’4236981889937278161526466314385615958256881888899512721588426754199503412587065565
’1048705376814767265132557470407658574792912915723345106432450947150072296210941943
’984760375594985848253359305585439638443’,
’generator’=> ’2’,
’private’ => ’9920931406657259523640856959196798855714124956149426748625180803553539633227862014
’81312712891672623072630995180324388841681491857745515696789091127409515009250358965
’46342049838178521379132153348139908016819196219448310107072632515749339055798122538
’04828702523796951800575031871051678091’
);

14
15
16
17

$bobOptions
= array(
’prime’
=> $aliceOptions[’prime’],
’generator’=> ’2’,

291
Zend Framework 2 Documentation, Release 2.2.5

292

Chapter 64. Public key cryptography
Zend Framework 2 Documentation, Release 2.2.5

’private’

18
19
20
21
22

=> ’3341173579263955862573363571789256361254818065040216115107747831484146370794889978
’1232563473041055194677275288017786897281696355182174038670007603421340815392469256
’6346473315660054548451083307242700347420706465071483108330449773716038209708335687
’31616972608703322302585471319261275664’

);

23
24
25

$alice = new DiffieHellman($aliceOptions[’prime’], $aliceOptions[’generator’], $aliceOptions[’private
$bob
= new DiffieHellman($bobOptions[’prime’], $bobOptions[’generator’], $bobOptions[’private’]);

26
27
28

$alice->generateKeys();
$bob->generateKeys();

29
30
31
32

$aliceSecretKey = $alice->computeSecretKey($bob->getPublicKey(DiffieHellman::FORMAT_BINARY),
DiffieHellman::FORMAT_BINARY,
DiffieHellman::FORMAT_BINARY);

33
34

$bobSecretKey

35
36

= $bob->computeSecretKey($alice->getPublicKey(DiffieHellman::FORMAT_BINARY),
DiffieHellman::FORMAT_BINARY,
DiffieHellman::FORMAT_BINARY);

37
38
39
40
41
42

if ($aliceSecretKey !== $bobSecretKey) {
echo "ERROR!n";
} else {
printf("The secret key is: %sn", base64_encode($aliceSecretKey));
}

The parameters of the Diffie-Hellman class are: a prime number (p), a generator (g) that is a primitive root mod p
and a private integer number. The security of the Diffie-Hellman exchange algorithm is related to the choice of these
parameters. To know how to choose secure numbers you can read the RFC 3526 document.
Note: The ZendCryptPublicKeyDiffieHellman class use by default the OpenSSL extension of PHP to
generate the parameters. If you don’t want to use the OpenSSL library you have to set the useOpensslExtension
static method to false.

64.2 RSA
RSA is an algorithm for public-key cryptography that is based on the presumed difficulty of factoring large integers,
the factoring problem. A user of RSA creates and then publishes the product of two large prime numbers, along with
an auxiliary value, as their public key. The prime factors must be kept secret. Anyone can use the public key to encrypt
a message, but with currently published methods, if the public key is large enough, only someone with knowledge of
the prime factors can feasibly decode the message. Whether breaking RSA encryption is as hard as factoring is an
open question known as the RSA problem.
The RSA algorithm can be used to encrypt/decrypt message and also to provide authenticity and integrity generating
a digital signature of a message. Suppose that Alice wants to send an encrypted message to Bob. Alice must use
the public key of Bob to encrypt the message. Bob can decrypt the message using his private key. Because Bob he
is the only one that can access to his private key, he is the only one that can decrypt the message. If Alice wants to
provide authenticity and integrity of a message to Bob she can use her private key to sign the message. Bob can check
the correctness of the digital signature using the public key of Alice. Alice can provide encryption, authenticity and
integrity of a message to Bob using the previous schemas in sequence, applying the encryption first and the digital
signature after.
Below we reported some examples of usage of the ZendCryptPublicKeyRsa class in order to:
• generate a public key and a private key;
64.2. RSA

293
Zend Framework 2 Documentation, Release 2.2.5

• encrypt/decrypt a string;
• generate a digital signature of a file.

64.2.1 Generate a public key and a private key
In order to generate a public and private key you can use the following code:
1

use ZendCryptPublicKeyRsaOptions;

2
3
4
5

$rsaOptions = new RsaOptions(array(
’pass_phrase’ => ’test’
));

6
7
8
9

$rsaOptions->generateKeys(array(
’private_key_bits’ => 2048,
));

10
11
12

file_put_contents(’private_key.pem’, $rsaOptions->getPrivateKey());
file_put_contents(’public_key.pub’, $rsaOptions->getPublicKey());

This example generates a public and private key of 2048 bit storing the keys in two separate files, the
private_key.pem for the private key and the public_key.pub for the public key. You can also generate
the public and private key using OpenSSL from the command line (Unix style syntax):
ssh-keygen -t rsa

64.2.2 Encrypt and decrypt a string
Below is reported an example on how to encrypt and decrypt a string using the RSA algorithm. You can encrypt only
small strings. The maximum size of encryption is given by the length of the public/private key - 88 bits. For instance,
if we use a size of 2048 bit you can encrypt string with a maximum size of 1960 bit (245 characters). This limitation
is related to the OpenSSL implementation for a security reason related to the nature of the RSA algorithm.
The normal application of a public key encryption algorithm is to store a key or a hash of the data you want to
respectively encrypt or sign. A hash is typically 128-256 bits (the PHP sha1() function returns a 160 bit hash). An
AES encryption key is 128 to 256 bits. So either of those will comfortably fit inside a single RSA encryption.
1

use ZendCryptPublicKeyRsa;

2
3
4
5
6
7
8

$rsa = Rsa::factory(array(
’public_key’
=> ’public_key.pub’,
’private_key’
=> ’private_key.pem’,
’pass_phrase’
=> ’test’,
’binary_output’ => false
));

9
10

$text = ’This is the message to encrypt’;

11
12
13

$encrypt = $rsa->encrypt($text);
printf("Encrypted message:n%sn", $encrypt);

14
15

$decrypt = $rsa->decrypt($encrypt);

16
17
18
19

if ($text !== $decrypt) {
echo "ERRORn";
} else {

294

Chapter 64. Public key cryptography
Zend Framework 2 Documentation, Release 2.2.5

echo "Encryption and decryption performed successfully!n";

20
21

}

64.2.3 Generate a digital signature of a file
Below is reported an example of how to generate a digital signature of a file.
1

use ZendCryptPublicKeyRsa;

2
3
4
5
6
7

$rsa = Rsa::factory(array(
’private_key’
=> ’path/to/private_key’,
’pass_phrase’
=> ’passphrase of the private key’,
’binary_output’ => false
));

8
9

$file = file_get_contents(’path/file/to/sign’);

10
11
12

$signature = $rsa->sign($file, $rsa->getOptions()->getPrivateKey());
$verify
= $rsa->verify($file, $signature, $rsa->getOptions()->getPublicKey());

13
14
15
16
17
18
19
20

if ($verify) {
echo "The signature is OKn";
file_put_contents($filename . ’.sig’, $signature);
echo "Signature save in $filename.sign";
} else {
echo "The signature is not valid!n";
}

In this example we used the Base64 format to encode the digital signature of the file (binary_output is false).
Note: The implementation of ZendCryptPublicKeyRsa algorithm uses the OpenSSL extension of PHP.

64.2. RSA

295
Zend Framework 2 Documentation, Release 2.2.5

296

Chapter 64. Public key cryptography
CHAPTER 65

ZendDbAdapter

The Adapter object is the most important sub-component of ZendDb. It is responsible for adapting any code written
in or for ZendDb to the targeted php extensions and vendor databases. In doing this, it creates an abstraction layer
for the PHP extensions, which is called the “Driver” portion of the ZendDb adapter. It also creates a lightweight
abstraction layer, called the “Platform” portion of the adapter, for the various idiosyncrasies that each vendor-specific
platform might have in its SQL/RDBMS implementation.

65.1 Creating an Adapter - Quickstart
Creating an adapter can simply be done by instantiating the ZendDbAdapterAdapter class. The most common use case, while not the most explicit, is to pass an array of configuration to the Adapter.
1

$adapter = new ZendDbAdapterAdapter($configArray);

This driver array is an abstraction for the extension level required parameters. Here is a table for the key-value pairs
that should be in configuration array.
Key
driver
database
username
password
hostname
port
charset

Is Required?
required
generally required
generally required
generally required
not generally required
not generally required
not generally required

Value
Mysqli, Sqlsrv, Pdo_Sqlite, Pdo_Mysql, Pdo=OtherPdoDriver
the name of the database (schema)
the connection username
the connection password
the IP address or hostname to connect to
the port to connect to (if applicable)
the character set to use

Note: Other names will work as well. Effectively, if the PHP manual uses a particular naming, this naming will be
supported by our Driver. For example, dbname in most cases will also work for ‘database’. Another example is that
in the case of Sqlsrv, UID will work in place of username. Which format you chose is up to you, but the above table
represents the official abstraction names.
So, for example, a MySQL connection using ext/mysqli:
1
2
3
4

$adapter = new ZendDbAdapterAdapter(array(
’driver’ => ’Mysqli’,
’database’ => ’zend_db_example’,
’username’ => ’developer’,

297
Zend Framework 2 Documentation, Release 2.2.5

5
6

’password’ => ’developer-password’
));

Another example, of a Sqlite connection via PDO:
1
2
3
4

$adapter = new ZendDbAdapterAdapter(array(
’driver’ => ’Pdo_Sqlite’,
’database’ => ’path/to/sqlite.db’
));

It is important to know that by using this style of adapter creation, the Adapter will attempt to create any dependencies that were not explicitly provided. A Driver object will be created from the configuration array provided in the
constructor. A Platform object will be created based off the type of Driver class that was instantiated. And lastly, a
default ResultSet object is created and utilized. Any of these objects can be injected, to do this, see the next section.
The list of officially supported drivers:
• Mysqli: The ext/mysqli driver
• Pgsql: The ext/pgsql driver
• Sqlsrv: The ext/sqlsrv driver (from Microsoft)
• Pdo_Mysql: MySQL through the PDO extension
• Pdo_Sqlite: SQLite though the PDO extension
• Pdo_Pgsql: PostgreSQL through the PDO extension

65.2 Creating an Adapter Using Dependency Injection
The more expressive and explicit way of creating an adapter is by injecting all your dependencies up front.
ZendDbAdapterAdapter uses constructor injection, and all required dependencies are injected through the
constructor, which has the following signature (in pseudo-code):
1
2

use ZendDbAdapterPlatformPlatformInterface;
use ZendDbResultSetResultSet;

3
4
5
6

class ZendDbAdapterAdapter {
public function __construct($driver, PlatformInterface $platform = null, ResultSet $queryResultSe
}

What can be injected:
• $driver
an
array
of
connection
parameters
ZendDbAdapterDriverDriverInterface

(see

above)

or

an

instance

of

• $platform - (optional) an instance of ZendDbPlatformPlatformInterface, the default will be created based off the driver implementation
• $queryResultSetPrototype - (optional) an instance of ZendDbResultSetResultSet, to understand
this object’s role, see the section below on querying through the adapter

65.3 Query Preparation Through ZendDbAdapterAdapter::query()
By default, query() prefers that you use “preparation” as a means for processing SQL statements. This generally means
that you will supply a SQL statement with the values substituted by placeholders, and then the parameters for those
placeholders are supplied separately. An example of this workflow with ZendDbAdapterAdapter is:
298

Chapter 65. ZendDbAdapter
Zend Framework 2 Documentation, Release 2.2.5

1

$adapter->query(’SELECT * FROM ‘artist‘ WHERE ‘id‘ = ?’, array(5));

The above example will go through the following steps:
• create a new Statement object
• prepare an array into a ParameterContainer if necessary
• inject the ParameterContainer into the Statement object
• execute the Statement object, producing a Result object
• check the Result object to check if the supplied sql was a “query”, or a result set producing statement
• if it is a result set producing query, clone the ResultSet prototype, inject Result as datasource, return it
• else, return the Result

65.4 Query Execution Through ZendDbAdapterAdapter::query()
In some cases, you have to execute statements directly. The primary purpose for needing to execute sql instead of
prepare and execute a sql statement, might be because you are attempting to execute a DDL statement (which in most
extensions and vendor platforms), are un-preparable. An example of executing:
1

$adapter->query(’ALTER TABLE ADD INDEX(‘foo_index‘) ON (‘foo_column‘)’, Adapter::QUERY_MODE_EXECUTE);

The primary difference to notice is that you must provide the Adapter::QUERY_MODE_EXECUTE (execute) as the
second parameter.

65.5 Creating Statements
While query() is highly useful for one-off and quick querying of a database through Adapter, it generally makes more
sense to create a statement and interact with it directly, so that you have greater control over the prepare-then-execute
workflow. To do this, Adapter gives you a routine called createStatement() that allows you to create a Driver specific
Statement to use so you can manage your own prepare-then-execute workflow.
1
2
3

// with optional parameters to bind up-front
$statement = $adapter->createStatement($sql, $optionalParameters);
$result = $statement->execute();

65.6 Using the Driver Object
The Driver object is the primary place where ZendDbAdapterAdapter implements the connection level abstraction making it possible to use all of ZendDb’s interfaces via the various ext/mysqli, ext/sqlsrv, PDO, and other
PHP level drivers. To make this possible, each driver is composed of 3 objects:
• A connection: ZendDbAdapterDriverConnectionInterface
• A statement: ZendDbAdapterDriverStatementInterface
• A result: ZendDbAdapterDriverResultInterface
Each of the built-in drivers practices “prototyping” as a means of creating objects when new instances are requested.
The workflow looks like this:
• An adapter is created with a set of connection parameters
65.4. Query Execution Through ZendDbAdapterAdapter::query()

299
Zend Framework 2 Documentation, Release 2.2.5

• The adapter chooses the proper driver to instantiate, for example ZendDbAdapterDriverMysqli
• That driver class is instantiated
• If no connection, statement or result objects are injected, defaults are instantiated
This driver is now ready to be called on when particular workflows are requested. Here is what the Driver API looks
like:
1

namespace ZendDbAdapterDriver;

2

interface DriverInterface
{
const PARAMETERIZATION_POSITIONAL = ’positional’;
const PARAMETERIZATION_NAMED = ’named’;
const NAME_FORMAT_CAMELCASE = ’camelCase’;
const NAME_FORMAT_NATURAL = ’natural’;
public function getDatabasePlatformName($nameFormat = self::NAME_FORMAT_CAMELCASE);
public function checkEnvironment();
public function getConnection();
public function createStatement($sqlOrResource = null);
public function createResult($resource);
public function getPrepareType();
public function formatParameterName($name, $type = null);
public function getLastGeneratedValue();
}

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

From this DriverInterface, you can
• Determine the name of the platform this driver supports (useful for choosing the proper platform object)
• Check that the environment can support this driver
• Return the Connection object
• Create a Statement object which is optionally seeded by an SQL statement (this will generally be a clone of a
prototypical statement object)
• Create a Result object which is optionally seeded by a statement resource (this will generally be a clone of a
prototypical result object)
• Format parameter names, important to distinguish the difference between the various ways parameters are named
between extensions
• Retrieve the overall last generated value (such as an auto-increment value)
Statement objects generally look like this:
1

namespace ZendDbAdapterDriver;

2
3
4
5
6
7
8

interface StatementInterface extends StatementContainerInterface
{
public function getResource();
public function prepare($sql = null);
public function isPrepared();
public function execute($parameters = null);

9

/** Inherited from StatementContainerInterface */
public function setSql($sql);
public function getSql();
public function setParameterContainer(ParameterContainer $parameterContainer);
public function getParameterContainer();

10
11
12
13
14
15

}

300

Chapter 65. ZendDbAdapter
Zend Framework 2 Documentation, Release 2.2.5

Result objects generally look like this:
1

namespace ZendDbAdapterDriver;

2
3
4
5
6
7
8
9
10
11

interface ResultInterface extends Countable, Iterator
{
public function buffer();
public function isQueryResult();
public function getAffectedRows();
public function getGeneratedValue();
public function getResource();
public function getFieldCount();
}

65.7 Using The Platform Object
The Platform object provides an API to assist in crafting queries in a way that is specific to the SQL implementation
of a particular vendor. Nuances such as how identifiers or values are quoted, or what the identifier separator character
is are handled by this object. To get an idea of the capabilities, the interface for a platform object looks like this:
1

namespace ZendDbAdapterPlatform;

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

interface PlatformInterface
{
public function getName();
public function getQuoteIdentifierSymbol();
public function quoteIdentifier($identifier);
public function quoteIdentifierChain($identiferChain)
public function getQuoteValueSymbol();
public function quoteValue($value);
public function quoteValueList($valueList);
public function getIdentifierSeparator();
public function quoteIdentifierInFragment($identifier, array $additionalSafeWords = array());
}

While one can instantiate your own Platform object, generally speaking, it is easier to get the proper Platform instance
from the configured adapter (by default the Platform type will match the underlying driver implementation):
1
2
3

$platform = $adapter->getPlatform();
// or
$platform = $adapter->platform; // magic property access

The following is a couple of example of Platform usage:
1
2
3

/** @var $adapter ZendDbAdapterAdapter */
/** @var $platform ZendDbAdapterPlatformSql92 */
$platform = $adapter->getPlatform();

4
5
6

// "first_name"
echo $platform->quoteIdentifier(’first_name’);

7
8
9

// "
echo $platform->getQuoteIdentifierSymbol();

10
11
12

// "schema"."mytable"
echo $platform->quoteIdentifierChain(array(’schema’,’mytable’)));

13

65.7. Using The Platform Object

301
Zend Framework 2 Documentation, Release 2.2.5

14
15

// ’
echo $platform->getQuoteValueSymbol();

16
17
18

// ’myvalue’
echo $platform->quoteValue(’myvalue’);

19
20
21

// ’value’, ’Foo O’Bar’
echo $platform->quoteValueList(array(’value’,"Foo O’Bar")));

22
23
24

// .
echo $platform->getIdentifierSeparator();

25
26
27

// "foo" as "bar"
echo $platform->quoteIdentifierInFragment(’foo as bar’);

28
29
30
31

// additionally, with some safe words:
// ("foo"."bar" = "boo"."baz")
echo $platform->quoteIdentifierInFragment(’(foo.bar = boo.baz)’, array(’(’, ’)’, ’=’));

65.8 Using The Parameter Container
The ParameterContainer object is a container for the various parameters that need to be passed into a Statement object
to fulfill all the various parameterized parts of the SQL statement. This object implements the ArrayAccess interface.
Below is the ParameterContainer API:
namespace ZendDbAdapter;
class ParameterContainer implements Iterator, ArrayAccess, Countable {
public function __construct(array $data = array())
/** methods to interact with values */
public function offsetExists($name)
public function offsetGet($name)
public function offsetSetReference($name, $from)
public function offsetSet($name, $value, $errata = null)
public function offsetUnset($name)
/** set values from array (will reset first) */
public function setFromArray(Array $data)
/** methods to interact with value errata */
public function offsetSetErrata($name, $errata)
public function offsetGetErrata($name)
public function offsetHasErrata($name)
public function offsetUnsetErrata($name)
/** errata only iterator */
public function getErrataIterator()
/** get array with named keys */
public function getNamedArray()
/** get array with int keys, ordered by position */
public function getPositionalArray()
/** iterator: */

302

Chapter 65. ZendDbAdapter
Zend Framework 2 Documentation, Release 2.2.5

public
public
public
public
public
public

function
function
function
function
function
function

count()
current()
next()
key()
valid()
rewind()

/** merge existing array of parameters with existing parameters */
public function merge($parameters)
}

In addition to handling parameter names and values, the container will assist in tracking parameter types for PHP type
to SQL type handling. For example, it might be important that:
$container->offsetSet(’limit’, 5);

be bound as an integer. To achieve this, pass in the ParameterContainer::TYPE_INTEGER constant as the 3rd parameter:
$container->offsetSet(’limit’, 5, $container::TYPE_INTEGER);

This will ensure that if the underlying driver supports typing of bound parameters, that this translated information will
also be passed along to the actual php database driver.

65.9 Examples
Creating a Driver and Vendor portable Query, Preparing and Iterating Result
1

$adapter = new ZendDbAdapterAdapter($driverConfig);

2
3
4

$qi = function($name) use ($adapter) { return $adapter->platform->quoteIdentifier($name); };
$fp = function($name) use ($adapter) { return $adapter->driver->formatParameterName($name); };

5
6
7
8

$sql = ’UPDATE ’ . $qi(’artist’)
. ’ SET ’ . $qi(’name’) . ’ = ’ . $fp(’name’)
. ’ WHERE ’ . $qi(’id’) . ’ = ’ . $fp(’id’);

9
10
11

/** @var $statement ZendDbAdapterDriverStatementInterface */
$statement = $adapter->query($sql);

12
13
14
15
16

$parameters = array(
’name’ => ’Updated Artist’,
’id’ => 1
);

17
18

$statement->execute($parameters);

19
20

// DATA INSERTED, NOW CHECK

21
22
23
24
25

/* @var $statement ZendDbAdapterDriverStatementInterface */
$statement = $adapter->query(’SELECT * FROM ’
. $qi(’artist’)
. ’ WHERE id = ’ . $fp(’id’));

26
27
28

/* @var $results ZendDbResultSetResultSet */
$results = $statement->execute(array(’id’ => 1));

29

65.9. Examples

303
Zend Framework 2 Documentation, Release 2.2.5

30
31

$row = $results->current();
$name = $row[’name’];

304

Chapter 65. ZendDbAdapter
CHAPTER 66

ZendDbResultSet

ZendDbResultSet is a sub-component of ZendDb for abstracting the iteration of rowset
producing queries.
While data sources for this can be anything that is iterable, generally a
ZendDbAdapterDriverResultInterface based object is the primary source for retrieving data.
ZendDbResultSet‘s must implement the ZendDbResultSetResultSetInterface and all subcomponents of ZendDb that return a ResultSet as part of their API will assume an instance of a
ResultSetInterface should be returned. In most casts, the Prototype pattern will be used by consuming object
to clone a prototype of a ResultSet and return a specialized ResultSet with a specific data source injected. The interface
of ResultSetInterface looks like this:
1
2
3
4
5

interface ResultSetInterface extends Traversable, Countable
{
public function initialize($dataSource);
public function getFieldCount();
}

66.1 Quickstart
ZendDbResultSetResultSet is the most basic form of a ResultSet object that will expose each row
as either an ArrayObject-like object or an array of row data. By default, ZendDbAdapterAdapter
will use a prototypical ZendDbResultSetResultSet object for iterating when using the
ZendDbAdapterAdapter::query() method.
The
following
is
an
example
workflow
ZendDbAdapterAdapter::query():
1
2

similar

to

what

one

might

find

inside

use ZendDbAdapterDriverResultInterface;
use ZendDbResultSetResultSet;

3
4
5
6

$stmt = $driver->createStatement(’SELECT * FROM users’);
$stmt->prepare();
$result = $stmt->execute($parameters);

7
8
9
10

if ($result instanceof ResultInterface && $result->isQueryResult()) {
$resultSet = new ResultSet;
$resultSet->initialize($result);

11

305
Zend Framework 2 Documentation, Release 2.2.5

foreach ($resultSet as $row) {
echo $row->my_column . PHP_EOL;
}

12
13
14
15

}

66.2 ZendDbResultSetResultSet and ZendDbResultSetAbstractResultSet
For most purposes, either a instance of ZendDbResultSetResultSet or a derivative of
ZendDbResultSetAbstractResultSet will be being used.
The implementation of the
AbstractResultSet offers the following core functionality:
1
2
3
4
5

abstract class AbstractResultSet implements Iterator, ResultSetInterface
{
public function initialize($dataSource)
public function getDataSource()
public function getFieldCount()

6

/** Iterator */
public function
public function
public function
public function
public function

7
8
9
10
11
12

next()
key()
current()
valid()
rewind()

13

/** countable */
public function count()

14
15
16

/** get rows as array */
public function toArray()

17
18
19

}

66.3 ZendDbResultSetHydratingResultSet
ZendDbResultSetHydratingResultSet is a more flexible ResultSet object that allows the developer to choose an appropriate “hydration strategy” for getting row data into a target object. While iterating over
results, HydratingResultSet will take a prototype of a target object and clone it once for each row. The
HydratingResultSet will then hydrate that clone with the row data.
In the example below, rows from the database will be iterated, and during iteration, HydratingRowSet will use the
Reflection based hydrator to inject the row data directly into the protected members of the cloned UserEntity object:
1
2
3

use ZendDbAdapterDriverResultInterface;
use ZendDbResultSetHydratingResultSet;
use ZendStdlibHydratorReflection as ReflectionHydrator;

4
5
6
7
8
9
10
11
12

class UserEntity {
protected $first_name;
protected $last_name;
public function getFirstName() { return $this->first_name; }
public function getLastName() { return $this->last_name; }
public function setFirstName($first_name) { $this->first_name = $first_name; }
public function setLastName($last_name) { $this->last_name = $last_name; }
}

13

306

Chapter 66. ZendDbResultSet
Zend Framework 2 Documentation, Release 2.2.5

14
15
16

$stmt = $driver->createStatement($sql);
$stmt->prepare($parameters);
$result = $stmt->execute();

17
18
19
20

if ($result instanceof ResultInterface && $result->isQueryResult()) {
$resultSet = new HydratingResultSet(new ReflectionHydrator, new UserEntity);
$resultSet->initialize($result);

21

foreach ($resultSet as $user) {
echo $user->getFirstName() . ’ ’ . $user->getLastName() . PHP_EOL;
}

22
23
24
25

}

For more information, see the ZendStdlibHydrator documentation to get a better sense of the different strategies that can be employed in order to populate a target object.

66.3. ZendDbResultSetHydratingResultSet

307
Zend Framework 2 Documentation, Release 2.2.5

308

Chapter 66. ZendDbResultSet
CHAPTER 67

ZendDbSql

ZendDbSql is a SQL abstraction layer for building platform specific SQL queries via a object-oriented API. The
end result of an ZendDbSql object will be to either produce a Statement and Parameter container that represents the target query, or a full string that can be directly executed against the database platform. To achieve this,
ZendDbSql objects require a ZendDbAdapterAdapter object in order to produce the desired results.

67.1 ZendDbSqlSql (Quickstart)
As there are four primary tasks associated with interacting with a database (from the DML, or Data Manipulation
Language): selecting, inserting, updating and deleting. As such, there are four primary objects that developers can
interact or building queries, ZendDbSqlSelect, Insert, Update and Delete.
Since these four tasks are so closely related, and generally used together within the same application,
ZendDbSqlSql objects help you create them and produce the result you are attempting to achieve.
1
2
3
4
5
6

use ZendDbSqlSql;
$sql = new Sql($adapter);
$select = $sql->select();
$insert = $sql->insert();
$update = $sql->update();
$delete = $sql->delete();

//
//
//
//

@return
@return
@return
@return

ZendDbSqlSelect
ZendDbSqlInsert
ZendDbSqlUpdate
ZendDbSqlDelete

As a developer, you can now interact with these objects, as described in the sections below, to specialize each query.
Once they have been populated with values, they are ready to either be prepared or executed.
To prepare (using a Select object):
1
2
3
4
5

use ZendDbSqlSql;
$sql = new Sql($adapter);
$select = $sql->select();
$select->from(’foo’);
$select->where(array(’id’ => 2));

6
7
8

$statement = $sql->prepareStatementForSqlObject($select);
$results = $statement->execute();

To execute (using a Select object)

309
Zend Framework 2 Documentation, Release 2.2.5

1
2
3
4
5

use ZendDbSqlSql;
$sql = new Sql($adapter);
$select = $sql->select();
$select->from(’foo’);
$select->where(array(’id’ => 2));

6
7
8

$selectString = $sql->getSqlStringForSqlObject($select);
$results = $adapter->query($selectString, $adapter::QUERY_MODE_EXECUTE);

ZendDbSqlSql objects can also be bound to a particular table so that in getting a select, insert, update, or delete
object, they are all primarily seeded with the same table when produced.
1
2
3
4

use ZendDbSqlSql;
$sql = new Sql($adapter, ’foo’);
$select = $sql->select();
$select->where(array(’id’ => 2)); // $select already has the from(’foo’) applied

67.2 ZendDbSql’s Select, Insert, Update and Delete
Each of these objects implements the following (2) interfaces:
1
2
3
4
5
6

interface PreparableSqlInterface {
public function prepareStatement(Adapter $adapter, StatementInterface $statement);
}
interface SqlInterface {
public function getSqlString(PlatformInterface $adapterPlatform = null);
}

These are the functions you can call to either produce (a) a prepared statement, or (b) a string to be executed.

67.3 ZendDbSqlSelect
ZendDbSqlSelect is an object who’s primary function is to present a unified API for building platform specific
SQL SELECT queries. The class can be instantiated and consumed without ZendDbSqlSql:
1
2
3
4

use ZendDbSqlSelect;
$select = new Select();
// or, to produce a $select bound to a specific table
$select = new Select(’foo’);

If a table is provided to the Select object, then from() cannot be called later to change the name of the table.
Once you have a valid Select object, the following API can be used to further specify various select statement parts:
1
2
3
4
5
6
7
8
9

class Select extends AbstractSql implements SqlInterface, PreparableSqlInterface
{
const JOIN_INNER = ’inner’;
const JOIN_OUTER = ’outer’;
const JOIN_LEFT = ’left’;
const JOIN_RIGHT = ’right’;
const SQL_STAR = ’*’;
const ORDER_ASCENDING = ’ASC’;
const ORDER_DESCENDING = ’DESC’;

10

public $where; // @param Where $where

11

310

Chapter 67. ZendDbSql
Zend Framework 2 Documentation, Release 2.2.5

12

public
public
public
public
public
public
public
public
public
public

13
14
15
16
17
18
19
20
21
22
23

function
function
function
function
function
function
function
function
function
function

__construct($table = null);
from($table);
columns(array $columns, $prefixColumnsWithTable = true);
join($name, $on, $columns = self::SQL_STAR, $type = self::JOIN_INNER);
where($predicate, $combination = PredicatePredicateSet::OP_AND);
group($group);
having($predicate, $combination = PredicatePredicateSet::OP_AND);
order($order);
limit($limit);
offset($offset);

}

67.3.1 from():
1
2

// as a string:
$select->from(’foo’);

3
4
5

// as an array to specify an alias:
// produces SELECT "t".* FROM "table" AS "t"

6
7

$select->from(array(’t’ => ’table’));

8
9
10

// using a SqlTableIdentifier:
// same output as above

11
12

$select->from(new TableIdentifier(array(’t’ => ’table’)));

67.3.2 columns():
1
2

// as array of names
$select->columns(array(’foo’, ’bar’));

3
4
5

// as an associative array with aliases as the keys:
// produces ’bar’ AS ’foo’, ’bax’ AS ’baz’

6
7

$select->columns(array(’foo’ => ’bar’, ’baz’ => ’bax’));

67.3.3 join():
1
2
3
4
5
6

$select->join(
’foo’, // table name
’id = bar.id’, // expression to join on (will be quoted by platform object before insertion),
array(’bar’, ’baz’), // (optional) list of columns, same requirements as columns() above
$select::JOIN_OUTER // (optional), one of inner, outer, left, right also represented by constant
);

7
8
9
10

$select->from(array(’f’ => ’foo’))
->join(array(’b’ => ’bar’),
’f.foo_id = b.foo_id’);

67.3. ZendDbSqlSelect

// base table
// join table with alias
// join expression

311
Zend Framework 2 Documentation, Release 2.2.5

67.3.4 where(), having():
The ZendDbSqlSelect object provides bit of flexibility as it regards to what kind of parameters are acceptable
when calling where() or having(). The method signature is listed as:
1
2
3
4
5
6
7
8

/**
* Create where clause
*
* @param Where|Closure|string|array $predicate
* @param string $combination One of the OP_* constants from PredicatePredicateSet
* @return Select
*/
public function where($predicate, $combination = PredicatePredicateSet::OP_AND);

As you can see, there are a number of different ways to pass criteria to both having() and where().
If you provide a ZendDbSqlWhere object to where() or a ZendDbSqlHaving object to having(), the
internal objects for Select will be replaced completely. When the where/having() is processed, this object will be
iterated to produce the WHERE or HAVING section of the SELECT statement.
If you provide a Closure to where() or having(), this function will be called with the Select’s Where object as the
only parameter. So the following is possible:
1
2
3

$spec = function (Where $where) {
$where->like(’username’, ’ralph%’);
};

4
5

$select->where($spec);

If you provide a string, this string will be used to instantiate a ZendDbSqlPredicateExpression object
so that it’s contents will be applied as is. This means that there will be no quoting in the fragment provided.
Consider the following code:
1
2

// SELECT "foo".* FROM "foo" WHERE x = 5
$select->from(’foo’)->where(’x = 5’);

If you provide an array who’s values are keyed by an integer, the value can either be a string that will be then used to
build a PredicateExpression or any object that implements PredicatePredicateInterface. These
objects are pushed onto the Where stack with the $combination provided.
Consider the following code:
1
2

// SELECT "foo".* FROM "foo" WHERE x = 5 AND y = z
$select->from(’foo’)->where(array(’x = 5’, ’y = z’));

If you provide an array who’s values are keyed with a string, these values will be handled in the following:
• PHP value nulls will be made into a PredicateIsNull object
• PHP value array()s will be made into a PredicateIn object
• PHP value strings will be made into a PredicateOperator object such that the string key will be identifier,
and the value will target value.
Consider the following code:
1
2
3
4

// SELECT "foo".* FROM "foo" WHERE "c1" IS NULL AND "c2" IN (?, ?, ?) AND "c3" IS NOT NULL
$select->from(’foo’)->where(array(
’c1’ => null,
’c2’ => array(1, 2, 3),

312

Chapter 67. ZendDbSql
Zend Framework 2 Documentation, Release 2.2.5

new ZendDbSqlPredicateIsNotNull(’c3’)

5
6

));

67.3.5 order():
1
2

$select = new Select;
$select->order(’id DESC’); // produces ’id’ DESC

3
4
5
6

$select = new Select;
$select->order(’id DESC’)
->order(’name ASC, age DESC’); // produces ’id’ DESC, ’name’ ASC, ’age’ DESC

7
8
9

$select = new Select;
$select->order(array(’name ASC’, ’age DESC’)); // produces ’name’ ASC, ’age’ DESC

67.3.6 limit() and offset():
1
2
3

$select = new Select;
$select->limit(5); // always takes an integer/numeric
$select->offset(10); // similarly takes an integer/numeric

67.4 ZendDbSqlInsert
The Insert API:
1
2
3
4

class Insert implements SqlInterface, PreparableSqlInterface
{
const VALUES_MERGE = ’merge’;
const VALUES_SET
= ’set’;

5

public
public
public
public

6
7
8
9
10

function
function
function
function

__construct($table = null);
into($table);
columns(array $columns);
values(array $values, $flag = self::VALUES_SET);

}

Similarly to Select objects, the table can be set at construction time or via into().

67.4.1 columns():
1

$insert->columns(array(’foo’, ’bar’)); // set the valid columns

67.4.2 values():
1
2
3
4
5
6

// default behavior of values is to set the values
// successive calls will not preserve values from previous calls
$insert->values(array(
’col_1’ => ’value1’,
’col_2’ => ’value2’
));

67.4. ZendDbSqlInsert

313
Zend Framework 2 Documentation, Release 2.2.5

1
2

// merging values with previous calls
$insert->values(array(’col_2’ => ’value2’), $insert::VALUES_MERGE);

67.5 ZendDbSqlUpdate
1
2
3
4

class Update
{
const VALUES_MERGE = ’merge’;
const VALUES_SET
= ’set’;

5

public
public
public
public
public

6
7
8
9
10
11

$where; // @param Where $where
function __construct($table = null);
function table($table);
function set(array $values, $flag = self::VALUES_SET);
function where($predicate, $combination = PredicatePredicateSet::OP_AND);

}

67.5.1 set():
1

$update->set(array(’foo’ => ’bar’, ’baz’ => ’bax’));

67.5.2 where():
See where section below.

67.6 ZendDbSqlDelete
1
2
3
4
5
6
7

class Delete
{
public $where; // @param Where $where
public function __construct($table = null);
public function from($table);
public function where($predicate, $combination = PredicatePredicateSet::OP_AND);
}

67.6.1 where():
See where section below.

67.7 ZendDbSqlWhere & ZendDbSqlHaving
In the following, we will talk about Where, Having is implies as being the same API.
Effectively, Where and Having extend from the same base object, a Predicate (and PredicateSet). All of the parts that
make up a where or having that are and’ed or or’d together are called predicates. The full set of predicates is called
a PredicateSet. This object set generally contains the values (and identifiers) separate from the fragment they belong
to until the last possible moment when the statement is either used to be prepared (parameteritized), or executed. In
314

Chapter 67. ZendDbSql
Zend Framework 2 Documentation, Release 2.2.5

parameterization, the parameters will be replaced with their proper placeholder (a named or positional parameter),
and the values stored inside a AdapterParameterContainer. When executed, the values will be interpolated into the
fragments they belong to and properly quoted.
It is important to know that in this API, a distinction is made between what elements are considered identifiers
(TYPE_IDENTIFIER) and which of those is a value (TYPE_VALUE). There is also a special use case type for literal
values (TYPE_LITERAL). These are all exposed via the ZendDbSqlExpressionInterface interface.
Note: In ZF 2.1, an actual Literal type was added. ZendDbSql now makes the distinction that Literals will not
have any parameters that need interpolating whereas it is expected that Expression objects might have parameters
that need interpolating. In cases where there are parameters in an Expression, ZendDbSqlAbstractSql
will do its best to identify placeholders when the Expression is processed during statement creation. In short, if you
don’t have parameters, use Literal objects.
The ZendDbSqlWhere (Predicate/PredicateSet) API:
1
2
3
4
5
6
7
8
9

// Where & Having:
class Predicate extends PredicateSet
{
public $and;
public $or;
public $AND;
public $OR;
public $NEST;
public $UNNEST;

10

public
public
public
public
public
public
public
public
public
public
public
public
public
public
public

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

function
function
function
function
function
function
function
function
function
function
function
function
function
function
function

nest();
setUnnest(Predicate $predicate);
unnest();
equalTo($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightType = self::TYP
lessThan($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightType = self::TY
greaterThan($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightType = self:
lessThanOrEqualTo($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightType =
greaterThanOrEqualTo($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightTyp
like($identifier, $like);
literal($literal);
expression($expression, $parameter);
isNull($identifier);
isNotNull($identifier);
in($identifier, array $valueSet = array());
between($identifier, $minValue, $maxValue);

26
27

// Inherited From PredicateSet

28
29

public
public
public
public
public
public

30
31
32
33
34
35
36

function
function
function
function
function
function

addPredicate(PredicateInterface $predicate, $combination = null);
getPredicates();
orPredicate(PredicateInterface $predicate);
andPredicate(PredicateInterface $predicate);
getExpressionData();
count();

}

Each method in the Where API will produce a corresponding Predicate object of a similarly named type, described
below, with the full API of the object:

67.7. ZendDbSqlWhere & ZendDbSqlHaving

315
Zend Framework 2 Documentation, Release 2.2.5

67.7.1 equalTo(), lessThan(), greaterThan(), lessThanOrEqualTo(), greaterThanOrEqualTo():
1

$where->equalTo(’id’, 5);

2
3
4
5
6

// same as the following workflow
$where->addPredicate(
new PredicateOperator($left, Operator::OPERATOR_EQUAL_TO, $right, $leftType, $rightType)
);

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

class Operator implements PredicateInterface
{
const OPERATOR_EQUAL_TO
const OP_EQ
const OPERATOR_NOT_EQUAL_TO
const OP_NE
const OPERATOR_LESS_THAN
const OP_LT
const OPERATOR_LESS_THAN_OR_EQUAL_TO
const OP_LTE
const OPERATOR_GREATER_THAN
const OP_GT
const OPERATOR_GREATER_THAN_OR_EQUAL_TO
const OP_GTE

=
=
=
=
=
=
=
=
=
=
=
=

’=’;
’=’;
’!=’;
’!=’;
’<’;
’<’;
’<=’;
’<=’;
’>’;
’>’;
’>=’;
’>=’;

22

public
public
public
public
public
public
public
public
public
public
public
public

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

function
function
function
function
function
function
function
function
function
function
function
function

__construct($left = null, $operator = self::OPERATOR_EQUAL_TO, $right = null, $le
setLeft($left);
getLeft();
setLeftType($type);
getLeftType();
setOperator($operator);
getOperator();
setRight($value);
getRight();
setRightType($type);
getRightType();
getExpressionData();

}

67.7.2 like($identifier, $like):
1

$where->like($identifier, $like):

2
3
4
5
6

// same as
$where->addPredicate(
new PredicateLike($identifier, $like)
);

7
8

// full API

9
10
11
12
13
14
15

class Like
{
public
public
public
public

316

implements PredicateInterface
function
function
function
function

__construct($identifier = null, $like = null);
setIdentifier($identifier);
getIdentifier();
setLike($like);

Chapter 67. ZendDbSql
Zend Framework 2 Documentation, Release 2.2.5

public function getLike();

16
17

}

67.7.3 literal($literal);
1

$where->literal($literal);

2
3
4
5
6

// same as
$where->addPredicate(
new PredicateLiteral($literal)
);

7
8
9
10
11
12
13
14
15

// full API
class Literal implements ExpressionInterface, PredicateInterface
{
const PLACEHOLDER = ’?’;
public function __construct($literal = ’’);
public function setLiteral($literal);
public function getLiteral();
}

67.7.4 expression($expression, $parameter);
1

$where->expression($expression, $parameter);

2
3
4
5
6

// same as
$where->addPredicate(
new PredicateExpression($expression, $parameter)
);

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

// full API
class Expression implements ExpressionInterface, PredicateInterface
{
const PLACEHOLDER = ’?’;
public function __construct($expression = null, $valueParameter = null /*[, $valueParameter, ...
public function setExpression($expression);
public function getExpression();
public function setParameters($parameters);
public function getParameters();
public function setTypes(array $types);
public function getTypes();
}

67.7.5 isNull($identifier);
1

$where->isNull($identifier);

2
3
4
5
6

// same as
$where->addPredicate(
new PredicateIsNull($identifier)
);

7
8

// full API

67.7. ZendDbSqlWhere & ZendDbSqlHaving

317
Zend Framework 2 Documentation, Release 2.2.5

9
10
11
12
13
14

class IsNull implements PredicateInterface
{
public function __construct($identifier = null);
public function setIdentifier($identifier);
public function getIdentifier();
}

67.7.6 isNotNull($identifier);
1

$where->isNotNull($identifier);

2
3
4
5
6

// same as
$where->addPredicate(
new PredicateIsNotNull($identifier)
);

7
8
9
10
11
12
13
14

// full API
class IsNotNull implements PredicateInterface
{
public function __construct($identifier = null);
public function setIdentifier($identifier);
public function getIdentifier();
}

67.7.7 in($identifier, array $valueSet = array());
1

$where->in($identifier, array $valueSet = array());

2
3
4
5
6

// same as
$where->addPredicate(
new PredicateIn($identifier, $valueSet)
);

7
8
9
10
11
12
13
14
15
16

// full API
class In implements
{
public function
public function
public function
public function
public function
}

PredicateInterface
__construct($identifier = null, array $valueSet = array());
setIdentifier($identifier);
getIdentifier();
setValueSet(array $valueSet);
getValueSet();

67.7.8 between($identifier, $minValue, $maxValue);
1

$where->between($identifier, $minValue, $maxValue);

2
3
4
5
6

// same as
$where->addPredicate(
new PredicateBetween($identifier, $minValue, $maxValue)
);

7
8

// full API

318

Chapter 67. ZendDbSql
Zend Framework 2 Documentation, Release 2.2.5

9
10
11
12
13
14
15
16
17
18
19

class Between implements PredicateInterface
{
public function __construct($identifier = null, $minValue = null, $maxValue = null);
public function setIdentifier($identifier);
public function getIdentifier();
public function setMinValue($minValue);
public function getMinValue();
public function setMaxValue($maxValue);
public function getMaxValue();
public function setSpecification($specification);
}

67.7. ZendDbSqlWhere & ZendDbSqlHaving

319
Zend Framework 2 Documentation, Release 2.2.5

320

Chapter 67. ZendDbSql
CHAPTER 68

ZendDbSqlDdl

ZendDbSqlDdl is a sub-component of ZendDbSql that allows consumers to create statement objects
that will produce DDL (Data Definition Language) SQL statements. When combined with a platform specific
ZendDbSqlSql object, these DDL objects are capable of producing platform-specific CREATE TABLE statements, with specialized data types, constraints, and indexes for a database/schema.
The following platforms have platform specializations for DDL:
• MySQL
• All databases compatible with ANSI SQL92

321
Zend Framework 2 Documentation, Release 2.2.5

322

Chapter 68. ZendDbSqlDdl
CHAPTER 69

Creating Tables

Like ZendDbSql objects, each statement type is represented by a class. For example, CREATE TABLE is modeled by a CreateTable object; this is likewise the same for ALTER TABLE (as AlterTable), and DROP TABLE
(as DropTable). These classes exist in the ZendDbSqlDdl namespace. To initiate the building of a DDL
statement, such as CreateTable, one needs to instantiate the object. There are a couple of valid patterns for this:
1

use ZendDbSqlDdl;

2
3

$table = new DdlCreateTable();

4
5
6

// or with table
$table = new DdlCreateTable(’bar’);

7
8
9

// optionally, as a temporary table
$table = new DdlCreateTable(’bar’, true);

You can also set the table after instantiation:
1

$table->setTable(’bar’);

Currently, columns are added by creating a column object, described in the data type table in the data type section
below:
1
2
3

use ZendDbSqlDdlColumn;
$table->addColumn(new ColumnInteger(’id’));
$table->addColumn(new ColumnVarchar(’name’, 255));

Beyond adding columns to a table, constraints can also be added:
1
2
3
4
5

use ZendDbSqlDdlConstraint;
$table->addConstraint(new ConstraintPrimaryKey(’id’));
$table->addConstraint(
new ConstraintUniqueKey([’name’, ’foo’], ’my_unique_key’)
);

323
Zend Framework 2 Documentation, Release 2.2.5

324

Chapter 69. Creating Tables
CHAPTER 70

Altering Tables

Similarly to CreateTable, you may also instantiate AlterTable:
1

use ZendDbSqlDdl;

2
3

$table = new DdlAlterTable();

4
5
6

// or with table
$table = new DdlAlterTable(’bar’);

7
8
9

// optionally, as a temporary table
$table = new DdlAlterTable(’bar’, true);

The primary difference between a CreateTable and AlterTable is that the AlterTable takes into account
that the table and its assets already exist. Therefore, while you still have addColumn() and addConstraint(),
you will also see the ability to change existing columns:
1
2

use ZendDbSqlDdlColumn;
$table->changeColumn(’name’, ColumnVarchar(’new_name’, 50));

You may also drop existing columns or constraints:
1
2

$table->dropColumn(’foo’);
$table->dropConstraint(’my_index’);

325
Zend Framework 2 Documentation, Release 2.2.5

326

Chapter 70. Altering Tables
CHAPTER 71

Dropping Tables

To drop a table, create a DropTable statement object:
1

$drop = new DdlDropTable(’bar’);

327
Zend Framework 2 Documentation, Release 2.2.5

328

Chapter 71. Dropping Tables
CHAPTER 72

Executing DDL Statements

After a DDL statement object has been created and configured, at some point you will want to execute the statement.
To do this, you will need two other objects: an Adapter instance, and a properly seeded Sql instance.
The workflow looks something like this, with $ddl being a CreateTable, AlterTable, or DropTable instance:
1

use ZendDbSqlSql;

2
3
4

// existence of $adapter is assumed
$sql = new Sql($adapter);

5
6
7
8
9

$adapter->query(
$sql->getSqlStringForSqlObject($ddl),
$adapter::QUERY_MODE_EXECUTE
);

By passing the $ddl object through the $sql object’s getSqlStringForSqlObject() method, we ensure that
any platform specific specializations/modifications are utilized to create a platform specific SQL statement.
Next, using the constant ZendDbAdapterAdapter::QUERY_MODE_EXECUTE ensures that the SQL statement is not prepared, as many DDL statements on a variety of platforms cannot be prepared, only executed.

329
Zend Framework 2 Documentation, Release 2.2.5

330

Chapter 72. Executing DDL Statements
CHAPTER 73

Currently Supported Data Types

These types exist in the ZendDbSqlDdlColumn namespace.
ZendDbSqlDdlColumnColumnInterface.

Data types must implement

In alphabetical order:
Type
Blob
Boolean
Char
Column
(generic)
Date
Decimal
Float
Integer
Time
Varchar

Arguments For Construction
$name, $length, $nullable = false, $default = null, array
$options = array()
$name
$name, $length
$name = null
$name
$name, $precision, $scale = null
$name, $digits, $decimal
$name, $nullable = false, $default = null, array $options =
array()
$name
$name, $length

Each of the above types can be utilized in any place that accepts a ColumnColumnInterface instance.
Currently, this is primarily in CreateTable::addColumn() and AlterTable‘s addColumn() and
changeColumn() methods.

331
Zend Framework 2 Documentation, Release 2.2.5

332

Chapter 73. Currently Supported Data Types
CHAPTER 74

Currently Supported Constraint Types

These types exist in the ZendDbSqlDdlConstraint namespace.
ZendDbSqlDdlConstraintConstraintInterface.

Data types must implement

In alphabetical order:
Type
Arguments For Construction
Check
$expression, $name
For$name, $column, $referenceTable, $referenceColumn, $onDeleteRule =
eignKey null, $onUpdateRule = null
Prima$columns
ryKey
UniqueKey $column, $name = null
Each of the above types can be utilized in any place that accepts a ColumnConstraintInterface instance.
Currently, this is primarily in CreateTable::addConstraint() and AlterTable::addConstraint().

333
Zend Framework 2 Documentation, Release 2.2.5

334

Chapter 74. Currently Supported Constraint Types
CHAPTER 75

ZendDbTableGateway

The Table Gateway object is intended to provide an object that represents a table in a database, and the methods of
this object mirror the most common operations on a database table. In code, the interface for such an object looks like
this:
1
2
3
4
5
6
7
8

interface ZendDbTableGatewayTableGatewayInterface
{
public function getTable();
public function select($where = null);
public function insert($set);
public function update($set, $where = null);
public function delete($where);
}

There are two primary implementations of the TableGatewayInterface that are of the most useful:
AbstractTableGateway and TableGateway. The AbstractTableGateway is an abstract basic implementation that provides functionality for select(), insert(), update(), delete(), as well as an additional API for doing these same kinds of tasks with explicit SQL objects. These methods are selectWith(),
insertWith(), updateWith() and deleteWith(). In addition, AbstractTableGateway also implements a
“Feature” API, that allows for expanding the behaviors of the base TableGateway implementation without having
to extend the class with this new functionality. The TableGateway concrete implementation simply adds a sensible
constructor to the AbstractTableGateway class so that out-of-the-box, TableGateway does not need to be
extended in order to be consumed and utilized to its fullest.

75.1 Basic Usage
The quickest way to get up and running with ZendDbTableGateway is to configure and utilize the concrete implementation of the TableGateway. The API of the concrete TableGateway is:
1
2
3
4
5

class TableGateway extends AbstractTableGateway
{
public $lastInsertValue;
public $table;
public $adapter;

6
7

public function __construct($table, Adapter $adapter, $features = null, ResultSet $resultSetProt

8
9

/** Inherited from AbstractTableGateway */

335
Zend Framework 2 Documentation, Release 2.2.5

10

public
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

function
function
function
function
function
function
function
function
function
function
function
function
function
function
function
function
function

isInitialized();
initialize();
getTable();
getAdapter();
getColumns();
getFeatureSet();
getResultSetPrototype();
getSql();
select($where = null);
selectWith(Select $select);
insert($set);
insertWith(Insert $insert);
update($set, $where = null);
updateWith(Update $update);
delete($where);
deleteWith(Delete $delete);
getLastInsertValue();

}

The concrete TableGateway object practices constructor injection for getting dependencies and options into the
instance. The table name and an instance of an Adapter are all that is needed to setup a working TableGateway
object.
Out of the box, this implementation makes no assumptions about table structure or metadata, and when select()
is executed, a simple ResultSet object with the populated Adapter’s Result (the datasource) will be returned and ready
for iteration.
1
2
3

use ZendDbTableGatewayTableGateway;
$projectTable = new TableGateway(’project’, $adapter);
$rowset = $projectTable->select(array(’type’ => ’PHP’));

4
5
6
7
8

echo ’Projects of type PHP: ’;
foreach ($rowset as $projectRow) {
echo $projectRow[’name’] . PHP_EOL;
}

9
10
11
12
13

// or, when expecting a single row:
$artistTable = new TableGateway(’artist’, $adapter);
$rowset = $artistTable->select(array(’id’ => 2));
$artistRow = $rowset->current();

14
15

var_dump($artistRow);

The select() method takes the same arguments as ZendDbSqlSelect::where() with the addition of
also being able to accept a closure, which in turn, will be passed the current Select object that is being used to build
the SELECT query. The following usage is possible:
1
2
3

use ZendDbTableGatewayTableGateway;
use ZendDbSqlSelect;
$artistTable = new TableGateway(’artist’, $adapter);

4
5
6
7
8
9

// search for at most 2 artists who’s name starts with Brit, ascending
$rowset = $artistTable->select(function (Select $select) {
$select->where->like(’name’, ’Brit%’);
$select->order(’name ASC’)->limit(2);
});

336

Chapter 75. ZendDbTableGateway
Zend Framework 2 Documentation, Release 2.2.5

75.2 TableGateway Features
The Features API allows for extending the functionality of the base TableGateway object without having to polymorphically extend the base class. This allows for a wider array of possible mixing and matching of features to
achieve a particular behavior that needs to be attained to make the base implementation of TableGateway useful
for a particular problem.
With the TableGateway object, features should be injected though the constructor. The constructor can take Features in 3 different forms: as a single feature object, as a FeatureSet object, or as an array of Feature objects.
There are a number of features built-in and shipped with ZendDb:
• GlobalAdapterFeature: the ability to use a global/static adapter without needing to inject it into a
TableGateway instance. This is more useful when you are extending the AbstractTableGateway implementation:
1
2

use ZendDbTableGatewayAbstractTableGateway;
use ZendDbTableGatewayFeature;

3
4
5
6
7
8
9
10
11
12
13

class MyTableGateway extends AbstractTableGateway
{
public function __construct()
{
$this->table = ’my_table’;
$this->featureSet = new FeatureFeatureSet();
$this->featureSet->addFeature(new FeatureGlobalAdapterFeature());
$this->initialize();
}
}

14
15
16

// elsewhere in code, in a bootstrap
ZendDbTableGatewayFeatureGlobalAdapterFeature::setStaticAdapter($adapter);

17
18
19

// in a controller, or model somewhere
$table = new MyTableGateway(); // adapter is statically loaded

• MasterSlaveFeature: the ability to use a master adapter for insert(), update(), and delete() while using a slave
adapter for all select() operations.
1

$table = new TableGateway(’artist’, $adapter, new FeatureMasterSlaveFeature($slaveAdapter));

• MetadataFeature: the ability populate TableGateway with column information from a Metadata object. It
will also store the primary key information in case RowGatewayFeature needs to consume this information.
1

$table = new TableGateway(’artist’, $adapter, new FeatureMetadataFeature());

• EventFeature: the ability utilize a TableGateway object with ZendEventManager and to be able to subscribe
to various events in a TableGateway lifecycle.
1

$table = new TableGateway(’artist’, $adapter, new FeatureEventFeature($eventManagerInstance));

• RowGatewayFeature: the ability for select() to return a ResultSet object that upon iteration will return a
RowGateway object for each row.
1
2

$table = new TableGateway(’artist’, $adapter, new FeatureRowGatewayFeature(’id’));
$results = $table->select(array(’id’ => 2));

3
4

$artistRow = $results->current();

75.2. TableGateway Features

337
Zend Framework 2 Documentation, Release 2.2.5

5
6

$artistRow->name = ’New Name’;
$artistRow->save();

338

Chapter 75. ZendDbTableGateway
CHAPTER 76

ZendDbRowGateway

ZendDbRowGateway is a sub-component of ZendDb that implements the Row Gateway pattern from PoEAA.
This effectively means that Row Gateway objects primarily model a row in a database, and have methods such as
save() and delete() that will help persist this row-as-an-object in the database itself. Likewise, after a row from the
database is retrieved, it can then be manipulated and save()’d back to the database in the same position (row), or it can
be delete()’d from the table.
The interface for a Row Gateway object simply adds save() and delete() and this is the interface that should be assumed
when a component has a dependency that is expected to be an instance of a RowGateway object:
1
2
3
4
5

interface RowGatewayInterface
{
public function save();
public function delete();
}

76.1 Quickstart
While most of the time, RowGateway will be used in conjunction with other ZendDbResultSet producing objects, it
is possible to use it standalone. To use it standalone, you simply need an Adapter and a set of data to work with. The
following use case demonstrates ZendDbRowGatewayRowGateway usage in its simplest form:
1

use ZendDbRowGatewayRowGateway;

2
3
4

// query the database
$resultSet = $adapter->query(’SELECT * FROM ‘user‘ WHERE ‘id‘ = ?’, array(2));

5
6
7

// get array of data
$rowData = $resultSet->current()->getArrayCopy();

8
9
10
11

// row gateway
$rowGateway = new RowGateway(’id’, ’my_table’, $adapter);
$rowGateway->populate($rowData);

12
13
14

$rowGateway->first_name = ’New Name’;
$rowGateway->save();

15

339
Zend Framework 2 Documentation, Release 2.2.5

16
17

// or delete this row:
$rowGateway->delete();

The workflow described above is greatly simplified when RowGateway is used in conjunction with the TableGateway
feature. What this achieves is a Table Gateway object that when select()’ing from a table, will produce a ResultSet
that is then capable of producing valid Row Gateway objects. Its usage looks like this:
1
2

use ZendDbTableGatewayFeatureRowGatewayFeature;
use ZendDbTableGatewayTableGateway;

3
4
5

$table = new TableGateway(’artist’, $adapter, new RowGatewayFeature(’id’));
$results = $table->select(array(’id’ => 2));

6
7
8
9

$artistRow = $results->current();
$artistRow->name = ’New Name’;
$artistRow->save();

76.2 ActiveRecord Style Objects
If you wish to have custom behaviour for your RowGateway objects (essentially making them behave similarly to the ActiveRecord pattern), pass a prototype object implementing the RowGatewayInterface to the
RowGatewayFeature constructor instead of a primary key:
1
2
3

use ZendDbTableGatewayFeatureRowGatewayFeature;
use ZendDbTableGatewayTableGateway;
use ZendDbRowGatewayRowGatewayInterface;

4
5
6
7

class Artist implements RowGatewayInterface
{
protected $adapter;

8

public function __construct($adapter)
{
$this->adapter = $adapter;
}

9
10
11
12
13

// ... save() and delete() implementations

14
15

}

16
17

$table = new TableGateway(’artist’, $adapter, new RowGatewayFeature(new Artist($adapter)));

340

Chapter 76. ZendDbRowGateway
CHAPTER 77

ZendDbMetadata

ZendDbMetadata is as sub-component of ZendDb that makes it possible to get metadata information about
tables, columns, constraints, triggers, and other information from a database in a standardized way. The primary
interface for the Metadata objects is:
1
2
3

interface MetadataInterface
{
public function getSchemas();

4

public function getTableNames($schema = null, $includeViews = false);
public function getTables($schema = null, $includeViews = false);
public function getTable($tableName, $schema = null);

5
6
7
8

public function getViewNames($schema = null);
public function getViews($schema = null);
public function getView($viewName, $schema = null);

9
10
11
12

public function getColumnNames($table, $schema = null);
public function getColumns($table, $schema = null);
public function getColumn($columnName, $table, $schema = null);

13
14
15
16

public function getConstraints($table, $schema = null);
public function getConstraint($constraintName, $table, $schema = null);
public function getConstraintKeys($constraint, $table, $schema = null);

17
18
19
20

public function getTriggerNames($schema = null);
public function getTriggers($schema = null);
public function getTrigger($triggerName, $schema = null);

21
22
23
24

}

77.1 Basic Usage
Usage of ZendDbMetadata is very straight forward. The top level class ZendDbMetadataMetadata will, given
an adapter, choose the best strategy (based on the database platform being used) for retrieving metadata. In most
cases, information will come from querying the INFORMATION_SCHEMA tables generally accessible to all database
connections about the currently accessible schema.

341
Zend Framework 2 Documentation, Release 2.2.5

Metadata::get*Names() methods will return an array of strings, while the other methods will return specific value
objects with the containing information. This is best demonstrated by the script below.
1

$metadata = new ZendDbMetadataMetadata($adapter);

2
3
4

// get the table names
$tableNames = $metadata->getTableNames();

5
6
7

foreach ($tableNames as $tableName) {
echo ’In Table ’ . $tableName . PHP_EOL;

8

$table = $metadata->getTable($tableName);

9
10
11

echo ’
With columns: ’ . PHP_EOL;
foreach ($table->getColumns() as $column) {
echo ’
’ . $column->getName()
. ’ -> ’ . $column->getDataType()
. PHP_EOL;
}

12
13
14
15
16
17
18

echo PHP_EOL;
echo ’
With constraints: ’ . PHP_EOL;

19
20
21

foreach ($metadata->getConstraints($tableName) as $constraint) {
/** @var $constraint ZendDbMetadataObjectConstraintObject */
echo ’
’ . $constraint->getName()
. ’ -> ’ . $constraint->getType()
. PHP_EOL;
if (!$constraint->hasColumns()) {
continue;
}
echo ’
column: ’ . implode(’, ’, $constraint->getColumns());
if ($constraint->isForeignKey()) {
$fkCols = array();
foreach ($constraint->getReferencedColumns() as $refColumn) {
$fkCols[] = $constraint->getReferencedTableName() . ’.’ . $refColumn;
}
echo ’ => ’ . implode(’, ’, $fkCols);
}
echo PHP_EOL;

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

}

40
41

echo ’----’ . PHP_EOL;

42
43

}

Metadata returns value objects that provide an interface to help developers better explore the metadata. Below is the
API for the various value objects:
The TableObject:
1
2
3
4
5
6
7

class ZendDbMetadataObjectTableObject
{
public function __construct($name);
public function setColumns(array $columns);
public function getColumns();
public function setConstraints($constraints);
public function getConstraints();

342

Chapter 77. ZendDbMetadata
Zend Framework 2 Documentation, Release 2.2.5

public function setName($name);
public function getName();

8
9
10

}

The ColumnObject:
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

class ZendDbMetadataObjectColumnObject {
public function __construct($name, $tableName, $schemaName = null);
public function setName($name);
public function getName();
public function getTableName();
public function setTableName($tableName);
public function setSchemaName($schemaName);
public function getSchemaName();
public function getOrdinalPosition();
public function setOrdinalPosition($ordinalPosition);
public function getColumnDefault();
public function setColumnDefault($columnDefault);
public function getIsNullable();
public function setIsNullable($isNullable);
public function isNullable();
public function getDataType();
public function setDataType($dataType);
public function getCharacterMaximumLength();
public function setCharacterMaximumLength($characterMaximumLength);
public function getCharacterOctetLength();
public function setCharacterOctetLength($characterOctetLength);
public function getNumericPrecision();
public function setNumericPrecision($numericPrecision);
public function getNumericScale();
public function setNumericScale($numericScale);
public function getNumericUnsigned();
public function setNumericUnsigned($numericUnsigned);
public function isNumericUnsigned();
public function getErratas();
public function setErratas(array $erratas);
public function getErrata($errataName);
public function setErrata($errataName, $errataValue);
}

The ConstraintObject:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

class ZendDbMetadataObjectConstraintObject
{
public function __construct($name, $tableName, $schemaName = null);
public function setName($name);
public function getName();
public function setSchemaName($schemaName);
public function getSchemaName();
public function getTableName();
public function setTableName($tableName);
public function setType($type);
public function getType();
public function hasColumns();
public function getColumns();
public function setColumns(array $columns);
public function getReferencedTableSchema();
public function setReferencedTableSchema($referencedTableSchema);
public function getReferencedTableName();

77.1. Basic Usage

343
Zend Framework 2 Documentation, Release 2.2.5

public
public
public
public
public
public
public
public
public
public
public
public
public
public
public

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

function
function
function
function
function
function
function
function
function
function
function
function
function
function
function

setReferencedTableName($referencedTableName);
getReferencedColumns();
setReferencedColumns(array $referencedColumns);
getMatchOption();
setMatchOption($matchOption);
getUpdateRule();
setUpdateRule($updateRule);
getDeleteRule();
setDeleteRule($deleteRule);
getCheckClause();
setCheckClause($checkClause);
isPrimaryKey();
isUnique();
isForeignKey();
isCheck();

33
34

}

The TriggerObject:
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

class ZendDbMetadataObjectTriggerObject
{
public function getName();
public function setName($name);
public function getEventManipulation();
public function setEventManipulation($eventManipulation);
public function getEventObjectCatalog();
public function setEventObjectCatalog($eventObjectCatalog);
public function getEventObjectSchema();
public function setEventObjectSchema($eventObjectSchema);
public function getEventObjectTable();
public function setEventObjectTable($eventObjectTable);
public function getActionOrder();
public function setActionOrder($actionOrder);
public function getActionCondition();
public function setActionCondition($actionCondition);
public function getActionStatement();
public function setActionStatement($actionStatement);
public function getActionOrientation();
public function setActionOrientation($actionOrientation);
public function getActionTiming();
public function setActionTiming($actionTiming);
public function getActionReferenceOldTable();
public function setActionReferenceOldTable($actionReferenceOldTable);
public function getActionReferenceNewTable();
public function setActionReferenceNewTable($actionReferenceNewTable);
public function getActionReferenceOldRow();
public function setActionReferenceOldRow($actionReferenceOldRow);
public function getActionReferenceNewRow();
public function setActionReferenceNewRow($actionReferenceNewRow);
public function getCreated();
public function setCreated($created);
}

344

Chapter 77. ZendDbMetadata
CHAPTER 78

Introduction to ZendDi

78.1 Dependency Injection
Dependency Injection (here-in called DI) is a concept that has been talked about in numerous places over the web.
Simply put, we’ll explain the act of injecting dependencies simply with this below code:
1

$b = new MovieLister(new MovieFinder());

Above, MovieFinder is a dependency of MovieLister, and MovieFinder was injected into MovieLister. If you are
not familiar with the concept of DI, here are a couple of great reads: Matthew Weier O’Phinney’s Analogy, Ralph
Schindler’s Learning DI, or Fabien Potencier’s Series on DI.

78.2 Dependency Injection Containers
When your code is written in such a way that all your dependencies are injected into consuming objects, you might
find that the simple act of wiring an object has gotten more complex. When this becomes the case, and you find that
this wiring is creating more boilerplate code, this makes for an excellent opportunity to utilize a Dependency Injection
Container.
In it’s simplest form, a Dependency Injection Container (here-in called a DiC for brevity), is an object that is capable of
creating objects on request and managing the “wiring”, or the injection of required dependencies, for those requested
objects. Since the patterns that developers employ in writing DI capable code vary, DiC’s are generally either in the
form of smallish objects that suit a very specific pattern, or larger DiC frameworks.
ZendDi is a DiC framework. While for the simplest code there is no configuration needed, and the use cases are quite
simple; for more complex code, ZendDi is capable of being configured to wire these complex use cases

345
Zend Framework 2 Documentation, Release 2.2.5

346

Chapter 78. Introduction to ZendDi
CHAPTER 79

ZendDi Quickstart

This QuickStart is intended to get developers familiar with the concepts of the ZendDi DiC. Generally speaking, code
is never as simple as it is inside this example, so working knowledge of the other sections of the manual is suggested.
Assume for a moment, you have the following code as part of your application that you feel is a good candidate for
being managed by a DiC, after all, you are already injecting all your dependencies:
1
2
3
4
5
6
7
8
9
10
11
12
13

namespace MyLibrary
{
class DbAdapter
{
protected $username = null;
protected $password = null;
public function __construct($username, $password)
{
$this->username = $username;
$this->password = $password;
}
}
}

14
15
16
17
18
19
20
21
22
23
24

namespace MyMovieApp
{
class MovieFinder
{
protected $dbAdapter = null;
public function __construct(MyLibraryDbAdapter $dbAdapter)
{
$this->dbAdapter = $dbAdapter;
}
}

25

class MovieLister
{
protected $movieFinder = null;
public function __construct(MovieFinder $movieFinder)
{
$this->movieFinder = $movieFinder;
}
}

26
27
28
29
30
31
32
33
34

}

347
Zend Framework 2 Documentation, Release 2.2.5

With the above code, you find yourself writing the following to wire and utilize this code:
1

// $config object is assumed

2
3
4
5
6
7
8

$dbAdapter = new MyLibraryDbAdapter($config->username, $config->password);
$movieFinder = new MyMovieAppMovieFinder($dbAdapter);
$movieLister = new MyMovieAppMovieLister($movieFinder);
foreach ($movieLister as $movie) {
// iterate and display $movie
}

If you are doing this above wiring in each controller or view that wants to list movies, not only can this become
repetitive and boring to write, but also unmaintainable if for example you want to swap out one of these dependencies
on a wholesale scale.
Since this example of code already practices good dependency injection, with constructor injection, it is a great candidate for using ZendDi. The usage is as simple as:
// inside a bootstrap somewhere
$di = new ZendDiDi();
$di->instanceManager()->setParameters(’MyLibraryDbAdapter’, array(
’username’ => $config->username,
’password’ => $config->password
));

1
2
3
4
5
6
7

// inside each controller
$movieLister = $di->get(’MyMovieAppMovieLister’);
foreach ($movieLister as $movie) {
// iterate and display $movie
}

8
9
10
11
12

In the above example, we are obtaining a default instance of ZendDiDi. By ‘default’, we mean that ZendDiDi is
constructed with a DefinitionList seeded with a RuntimeDefinition (uses Reflection) and an empty instance manager
and no configuration. Here is the ZendDiDi constructor:

public function __construct(DefinitionList $definitions = null, InstanceManager $instanceManager
{
$this->definitions = ($definitions) ?: new DefinitionList(new DefinitionRuntimeDefinition())
$this->instanceManager = ($instanceManager) ?: new InstanceManager();

1
2
3
4
5

if ($config) {
$this->configure($config);
}

6
7
8

}

9

This means that when $di->get() is called, it will be consulting the RuntimeDefinition, which uses reflection to understand the structure of the code. Once it knows the structure of the code, it can then know how the dependencies
fit together and how to go about wiring your objects for you. ZendDiDefinitionRuntimeDefinition will utilize the
names of the parameters in the methods as the class parameter names. This is how both username and password key
are mapped to the first and second parameter, respectively, of the constructor consuming these named parameters.
If you were to want to pass in the username and password at call time, this is achieved by passing them as the second
argument of get():
// inside each controller
$di = new ZendDiDi();
$movieLister = $di->get(’MyMovieAppMovieLister’, array(
’username’ => $config->username,
’password’ => $config->password
));

1
2
3
4
5
6

348

Chapter 79. ZendDi Quickstart
Zend Framework 2 Documentation, Release 2.2.5

7
8
9

foreach ($movieLister as $movie) {
// iterate and display $movie
}

It is important to note that when using call time parameters, these parameter names will be applied to any class that
accepts a parameter of such name.
By calling $di->get(), this instance of MovieLister will be automatically shared. This means subsequent calls to get()
will return the same instance as previous calls. If you wish to have completely new instances of MovieLister, you can
utilize $di->newInstance().

349
Zend Framework 2 Documentation, Release 2.2.5

350

Chapter 79. ZendDi Quickstart
CHAPTER 80

ZendDi Definition

Definitions are the place where ZendDi attempts to understand the structure of the code it is attempting to wire. This
means that if you’ve written non-ambiguous, clear and concise code; ZendDi has a very good chance of understanding
how to wire things up without much added complexity.

80.1 DefinitionList
Definitions are introduced to the ZendDiDi object through a definition list implemented as ZendDiDefinitionList
(SplDoublyLinkedList). Order is important. Definitions in the front of the list will be consulted on a class before
definitions at the end of the list.
Note: Regardless of what kind of Definition strategy you decide to use, it is important that your autoloaders are
already setup and ready to use.

80.2 RuntimeDefinition
The default DefinitionList instantiated by ZendDiDi, when no other DefinitionList is provided, has as DefinitionRuntimeDefinition baked-in. The RuntimeDefinition will respond to query’s about classes by using Reflection.
This Runtime definitions uses any available information inside methods: their signature, the names of parameters, the
type-hints of the parameters, and the default values to determine if something is optional or required when making a
call to that method. The more explicit you can be in your method naming and method signatures, the easier of a time
ZendDiDefinitionRuntimeDefinition will have determining the structure of your code.
This is what the constructor of a RuntimeDefinition looks like:
1
2
3
4
5
6
7

public function __construct(IntrospectionStrategy $introspectionStrategy = null, array $explicitClass
{
$this->introspectionStrategy = ($introspectionStrategy) ?: new IntrospectionStrategy();
if ($explicitClasses) {
$this->setExplicitClasses($explicitClasses);
}
}

The IntrospectionStrategy object is an object that determines the rules, or guidelines, for how the RuntimeDefinition
will introspect information about your classes. Here are the things it knows how to do:
351
Zend Framework 2 Documentation, Release 2.2.5

• Whether or not to use Annotations (Annotations are expensive and off by default, read more about these in the
Annotations section)
• Which method names to include in the introspection, by default, the pattern /^set[A-Z]{1}w*/ is registered by
default, this is a list of patterns.
• Which interface names represent the interface injection pattern. By default, the pattern /w*Awarew*/ is registered, this is a list of patterns.
The constructor for the IntrospectionStrategy looks like this:
1
2
3
4

public function __construct(AnnotationManager $annotationManager = null)
{
$this->annotationManager = ($annotationManager) ?: $this->createDefaultAnnotationManager();
}

This goes to say that an AnnotationManager is not required, but if you wish to create a special AnnotationManager
with your own annotations, and also wish to extend the RuntimeDefinition to look for these special Annotations, this
is the place to do it.
The RuntimeDefinition also can be used to look up either all classes (implicitly, which is default), or explicitly look up
for particular pre-defined classes. This is useful when your strategy for inspecting one set of classes might differ from
those of another strategy for another set of classes. This can be achieved by using the setExplicitClasses() method or
by passing a list of classes as a second argument to the constructor of the RuntimeDefinition.

80.3 CompilerDefinition
The CompilerDefinition is very much similar in nature to the RuntimeDefinition with the exception that it can be
seeded with more information for the purposes of “compiling” a definition. This is useful when you do not want to be
making all those (sometimes expensive) calls to reflection and the annotation scanning system during the request of
your application. By using the compiler, a definition can be created and written to disk to be used during a request, as
opposed to the task of scanning the actual code.
For example, let’s assume we want to create a script that will create definitions for some of our library code:
1
2
3
4
5

// in "package name" format
$components = array(
’My_MovieApp’,
’My_OtherClasses’,
);

6
7
8
9

foreach ($components as $component) {
$diCompiler = new ZendDiDefinitionCompilerDefinition;
$diCompiler->addDirectory(’/path/to/classes/’ . str_replace(’_’, ’/’, $component));

10

$diCompiler->compile();
file_put_contents(
__DIR__ . ’/../data/di/’ . $component . ’-definition.php’,
’<?php return ’ . var_export($diCompiler->toArrayDefinition()->toArray(), true) . ’;’
);

11
12
13
14
15
16

}

This will create a couple of files that will return an array of the definition for that class. To utilize this in an application,
the following code will suffice:
1
2
3

protected function setupDi(Application $app)
{
$definitionList = new DefinitionList(array(

352

Chapter 80. ZendDi Definition
Zend Framework 2 Documentation, Release 2.2.5

new DefinitionArrayDefinition(include __DIR__ . ’/path/to/data/di/My_MovieApp-definition.php
new DefinitionArrayDefinition(include __DIR__ . ’/path/to/data/di/My_OtherClasses-definition
$runtime = new DefinitionRuntimeDefinition(),

4
5
6

));
$di = new Di($definitionList, null, new Configuration($this->config->di));
$di->instanceManager()->addTypePreference(’ZendDiLocatorInterface’, $di);
$app->setLocator($di);

7
8
9
10
11

}

The above code would more than likely go inside your application’s or module’s bootstrap file. This represents the
simplest and most performant way of configuring your DiC for usage.

80.4 ClassDefinition
The idea behind using a ClassDefinition is two-fold. First, you may want to override some information inside of a
RuntimeDefinition. Secondly, you might want to simply define your complete class’s definition with an xml, ini, or
php file describing the structure. This class definition can be fed in via Configuration or by directly instantiating and
registering the Definition with the DefinitionList.
Todo - example

80.4. ClassDefinition

353
Zend Framework 2 Documentation, Release 2.2.5

354

Chapter 80. ZendDi Definition
CHAPTER 81

ZendDi InstanceManager

The InstanceManager is responsible for any runtime information associated with the ZendDiDi DiC. This means that
the information that goes into the instance manager is specific to both how the particular consuming Application’s
needs and even more specifically to the environment in which the application is running.

81.1 Parameters
Parameters are simply entry points for either dependencies or instance configuration values. A class consists of a set
of parameters, each uniquely named. When writing your classes, you should attempt to not use the same parameter
name twice in the same class when you expect that that parameters is used for either instance configuration or an object
dependency. This leads to an ambiguous parameter, and is a situation best avoided.
Our movie finder example can be further used to explain these concepts:
1
2
3
4
5
6
7
8
9
10
11
12
13

namespace MyLibrary
{
class DbAdapter
{
protected $username = null;
protected $password = null;
public function __construct($username, $password)
{
$this->username = $username;
$this->password = $password;
}
}
}

14
15
16
17
18
19
20
21
22
23
24

namespace MyMovieApp
{
class MovieFinder
{
protected $dbAdapter = null;
public function __construct(MyLibraryDbAdapter $dbAdapter)
{
$this->dbAdapter = $dbAdapter;
}
}

355
Zend Framework 2 Documentation, Release 2.2.5

25

class MovieLister
{
protected $movieFinder = null;
public function __construct(MovieFinder $movieFinder)
{
$this->movieFinder = $movieFinder;
}
}

26
27
28
29
30
31
32
33
34

}

In the above example, the class DbAdapter has 2 parameters: username and password; MovieFinder has one parameter:
dbAdapter, and MovieLister has one parameter: movieFinder. Any of these can be utilized for injection of either
dependencies or scalar values during instance configuration or during call time.
When looking at the above code, since the dbAdapter parameter and the movieFinder parameter are both type-hinted
with concrete types, the DiC can assume that it can fulfill these object tendencies by itself. On the other hand, username
and password do not have type-hints and are, more than likely, scalar in nature. Since the DiC cannot reasonably know
this information, it must be provided to the instance manager in the form of parameters. Not doing so will force
$di->get(‘MyMovieAppMovieLister’) to throw an exception.
The following ways of using parameters are available:
1
2
3
4
5

// setting instance configuration into the instance manager
$di->instanceManager()->setParameters(’MyLibraryDbAdapter’, array(
’username’ => ’myusername’,
’password’ => ’mypassword’
));

6
7
8
9
10

// forcing a particular dependency to be used by the instance manager
$di->instanceManager()->setParameters(’MyMovieAppMovieFinder’, array(
’dbAdapter’ => new MyLibraryDbAdapter(’myusername’, ’mypassword’)
));

11
12
13
14
15
16

// passing instance parameters at call time
$movieLister = $di->get(’MyMovieAppMovieLister’, array(
’username’ => $config->username,
’password’ => $config->password
));

17
18
19
20
21

// passing a specific instance at call time
$movieLister = $di->get(’MyMovieAppMovieLister’, array(
’dbAdapter’ => new MyLibraryDbAdapter(’myusername’, ’mypassword’)
));

81.2 Preferences
In some cases, you might be using interfaces as type hints as opposed to concrete types. Lets assume the movie
example was modified in the following way:
1
2
3
4
5
6

namespace MyMovieApp
{
interface MovieFinderInterface
{
// methods required for this type
}

7

356

Chapter 81. ZendDi InstanceManager
Zend Framework 2 Documentation, Release 2.2.5

class GenericMovieFinder implements MovieFinderInterface
{
protected $dbAdapter = null;
public function __construct(MyLibraryDbAdapter $dbAdapter)
{
$this->dbAdapter = $dbAdapter;
}
}

8
9
10
11
12
13
14
15
16

class MovieLister
{
protected $movieFinder = null;
public function __construct(MovieFinderInterface $movieFinder)
{
$this->movieFinder = $movieFinder;
}
}

17
18
19
20
21
22
23
24
25

}

What you’ll notice above is that now the MovieLister type minimally expects that the dependency injected implements
the MovieFinderInterface. This allows multiple implementations of this base interface to be used as a dependency,
if that is what the consumer decides they want to do. As you can imagine, ZendDi, by itself would not be able to
determine what kind of concrete object to use fulfill this dependency, so this type of ‘preference’ needs to be made
known to the instance manager.
To give this information to the instance manager, see the following code example:
1
2
3

$di->instanceManager()->addTypePreference(’MyMovieAppMovieFinderInterface’, ’MyMovieAppGenericMovie
// assuming all instance config for username, password is setup
$di->get(’MyMovieAppMovieLister’);

81.3 Aliases
In some situations, you’ll find that you need to alias an instance. There are two main reasons to do this. First, it creates
a simpler, alternative name to use when using the DiC, as opposed to using the full class name. Second, you might
find that you need to have the same object type in two separate contexts. This means that when you alias a particular
class, you can then attach a specific instance configuration to that alias; as opposed to attaching that configuration to
the class name.
To demonstrate both of these points, we’ll look at a use case where we’ll have two separate DbAdapters, one will be
for read-only operations, the other will be for read-write operations:
Note: Aliases can also have parameters registered at alias time
1

// assume the MovieLister example of code from the QuickStart

2
3

$im = $di->instanceManager();

4
5
6

// add alias for short naming
$im->addAlias(’movielister’, ’MyMovieAppMovieLister’);

7
8
9
10
11

// add aliases for specific instances
$im->addAlias(’dbadapter-readonly’, ’MyLibraryDbAdapter’, array(
’username’ => $config->db->readAdapter->username,
’password’ => $config->db->readAdapter->password,

81.3. Aliases

357
Zend Framework 2 Documentation, Release 2.2.5

12
13
14
15
16

));
$im->addAlias(’dbadapter-readwrite’, ’MyLibraryDbAdapter’, array(
’username’ => $config->db->readWriteAdapter->username,
’password’ => $config->db->readWriteAdapter->password,
));

17
18
19

// set a default type to use, pointing to an alias
$im->addTypePreference(’MyLibraryDbAdapter’, ’dbadapter-readonly’);

20
21
22

$movieListerRead = $di->get(’MyMovieAppMovieLister’);
$movieListerReadWrite = $di->get(’MyMovieAppMovieLister’, array(’dbAdapter’ => ’dbadapter-readwrite’

358

Chapter 81. ZendDi InstanceManager
CHAPTER 82

ZendDi Configuration

Most of the configuration for both the setup of Definitions as well as the setup of the Instance Manager can be attained
by a configuration file. This file will produce an array (typically) and have a particular iterable structure.
The top two keys are ‘definition’ and ‘instance’, each specifying values for respectively, definition setup and instance
manager setup.
The definition section expects the following information expressed as a PHP array:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

$config = array(
’definition’ => array(
’compiler’ => array(/* @todo compiler information */),
’runtime’ => array(/* @todo runtime information */),
’class’ => array(
’instantiator’ => ’’, // the name of the instantiator, by default this is __construct
’supertypes’
=> array(), // an array of supertypes the class implements
’methods’
=> array(
’setSomeParameter’ => array( // a method name
’parameterName’ => array(
’name’,
// string parameter name
’type’,
// type or null
’is-required’ // bool
)
)

16

)

17

)

18

)

19
20

);

359
Zend Framework 2 Documentation, Release 2.2.5

360

Chapter 82. ZendDi Configuration
CHAPTER 83

ZendDi Debugging & Complex Use
Cases

83.1 Debugging a DiC
It is possible to dump the information contained within both the Definition and InstanceManager for a Di instance.
The easiest way is to do the following:
1

ZendDiDisplayConsole::export($di);

If you are using a RuntimeDefinition where upon you expect a particular definition to be resolve at the first-call, you
can see that information to the console display to force it to read that class:
1

ZendDiDisplayConsole::export($di, array(’AClassIWantToGetTheDefinitionFor’));

83.2 Complex Use Cases
83.2.1 Interface Injection
1
2
3
4

namespace FooBar {
class Baz implements BamAwareInterface
{
public $bam;

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

public function setBam(Bam $bam)
{
$this->bam = $bam;
}
}
class Bam
{
}
interface BamAwareInterface
{
public function setBam(Bam $bam);

361
Zend Framework 2 Documentation, Release 2.2.5

}

17
18

}

19
20
21
22
23
24

namespace {
include ’zf2bootstrap.php’;
$di = new ZendDiDi;
$baz = $di->get(’FooBarBaz’);
}

83.2.2 Setter Injection with Class Definition
1
2
3
4

namespace FooBar {
class Baz
{
public $bam;

5

public function setBam(Bam $bam)
{
$this->bam = $bam;
}

6
7
8
9

}
class Bam {
}

10
11
12
13

}

14
15
16
17
18
19
20
21
22
23
24
25
26
27

namespace {
$di = new ZendDiDi;
$di->configure(new ZendDiConfig(array(
’definition’ => array(
’class’ => array(
’FooBarBaz’ => array(
’setBam’ => array(’required’ => true)
)
)
)
)));
$baz = $di->get(’FooBarBaz’);
}

83.2.3 Multiple Injections To A Single Injection Point
1
2
3
4

namespace Application {
class Page
{
public $blocks;

5

public function addBlock(Block $block)
{
$this->blocks[] = $block;
}

6
7
8
9

}
interface Block
{
}

10
11
12
13
14

}

362

Chapter 83. ZendDi Debugging & Complex Use Cases
Zend Framework 2 Documentation, Release 2.2.5

15
16
17
18
19

namespace MyModule {
class BlockOne implements ApplicationBlock {}
class BlockTwo implements ApplicationBlock {}
}

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

namespace {
include ’zf2bootstrap.php’;
$di = new ZendDiDi;
$di->configure(new ZendDiConfig(array(
’definition’ => array(
’class’ => array(
’ApplicationPage’ => array(
’addBlock’ => array(
’block’ => array(’type’ => ’ApplicationBlock’, ’required’ => true)
)
)
)
),
’instance’ => array(
’ApplicationPage’ => array(
’injections’ => array(
’MyModuleBlockOne’,
’MyModuleBlockTwo’
)
)
)
)));
$page = $di->get(’ApplicationPage’);
}

83.2. Complex Use Cases

363
Zend Framework 2 Documentation, Release 2.2.5

364

Chapter 83. ZendDi Debugging & Complex Use Cases
CHAPTER 84

Introduction to ZendDom

The ZendDom component provides tools for working with DOM documents and structures. Currently, we offer
ZendDomQuery, which provides a unified interface for querying DOM documents utilizing both XPath and CSS
selectors.

365
Zend Framework 2 Documentation, Release 2.2.5

366

Chapter 84. Introduction to ZendDom
CHAPTER 85

ZendDomQuery

ZendDomQuery provides mechanisms for querying XML and (X) HTML documents utilizing either XPath or
CSS selectors. It was developed to aid with functional testing of MVC applications, but could also be used for rapid
development of screen scrapers.
CSS selector notation is provided as a simpler and more familiar notation for web developers to utilize when querying
documents with XML structures. The notation should be familiar to anybody who has developed Cascading Style
Sheets or who utilizes Javascript toolkits that provide functionality for selecting nodes utilizing CSS selectors (Prototype’s $$() and Dojo’s dojo.query were both inspirations for the component).

85.1 Theory of Operation
To use ZendDomQuery, you instantiate a ZendDomQuery object, optionally passing a document to query (a
string). Once you have a document, you can use either the query() or queryXpath() methods; each method will
return a ZendDomNodeList object with any matching nodes.
The primary difference between ZendDomQuery and using DOMDocument + DOMXPath is the ability to select
against CSS selectors. You can utilize any of the following, in any combination:
• element types: provide an element type to match: ‘div’, ‘a’, ‘span’, ‘h2’, etc.
• style attributes: CSS style attributes to match: ‘.error‘, ‘div.error‘, ‘label.required‘, etc. If an
element defines more than one style, this will match as long as the named style is present anywhere in the style
declaration.
• id attributes: element ID attributes to match: ‘#content’, ‘div#nav’, etc.
• arbitrary attributes: arbitrary element attributes to match. Three different types of matching are provided:
– exact match: the attribute exactly matches the string: ‘div[bar=”baz”]’ would match a div element with a
“bar” attribute that exactly matches the value “baz”.
– word match: the attribute contains a word matching the string: ‘div[bar~=”baz”]’ would match a div
element with a “bar” attribute that contains the word “baz”. ‘<div bar=”foo baz”>’ would match, but ‘<div
bar=”foo bazbat”>’ would not.
– substring match: the attribute contains the string: ‘div[bar*=”baz”]’ would match a div element with a
“bar” attribute that contains the string “baz” anywhere within it.
• direct descendents: utilize ‘>’ between selectors to denote direct descendents. ‘div > span’ would select only
‘span’ elements that are direct descendents of a ‘div’. Can also be used with any of the selectors above.
367
Zend Framework 2 Documentation, Release 2.2.5

• descendents: string together multiple selectors to indicate a hierarchy along which to search. ‘div .foo
span #one‘ would select an element of id ‘one’ that is a descendent of arbitrary depth beneath a ‘span’
element, which is in turn a descendent of arbitrary depth beneath an element with a class of ‘foo’, that is an
descendent of arbitrary depth beneath a ‘div’ element. For example, it would match the link to the word ‘One’
in the listing below:
<div>
<table>
<tr>
<td class="foo">
<div>
Lorem ipsum <span class="bar">
<a href="/foo/bar" id="one">One</a>
<a href="/foo/baz" id="two">Two</a>
<a href="/foo/bat" id="three">Three</a>
<a href="/foo/bla" id="four">Four</a>
</span>
</div>
</td>
</tr>
</table>
</div>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

Once you’ve performed your query, you can then work with the result object to determine information about the nodes,
as well as to pull them and/or their content directly for examination and manipulation. ZendDomNodeList
implements Countable and Iterator, and stores the results internally as a DOMDocument and DOMNodeList.
As an example, consider the following call, that selects against the HTML above:
1

use ZendDomQuery;

2
3
4

$dom = new Query($html);
$results = $dom->execute(’.foo .bar a’);

5
6
7
8
9

$count = count($results); // get number of matches: 4
foreach ($results as $result) {
// $result is a DOMElement
}

ZendDomQuery also allows straight XPath queries utilizing the queryXpath() method; you can pass any valid
XPath query to this method, and it will return a ZendDomNodeList object.

85.2 Methods Available
The ZendDomQuery family of classes have the following methods available.

85.2.1 ZendDomQuery
The following methods are available to ZendDomQuery:
• setDocumentXml($document, $encoding = null): specify an XML string to query against.
• setDocumentXhtml($document, $encoding = null): specify an XHTML string to query against.
• setDocumentHtml($document, $encoding = null): specify an HTML string to query against.
• setDocument($document, $encoding = null):
specify
ZendDomQuery will then attempt to autodetect the document type.
368

a

string

to

query

against;

Chapter 85. ZendDomQuery
Zend Framework 2 Documentation, Release 2.2.5

• setEncoding($encoding): specify an encoding string to use. This encoding will be passed to DOMDocument’s constructor if specified.
• getDocument(): retrieve the original document string provided to the object.
• getDocumentType(): retrieve the document type of the document provided to the object; will be one of the
DOC_XML, DOC_XHTML, or DOC_HTML class constants.
• getEncoding(): retrieves the specified encoding.
• execute($query): query the document using CSS selector notation.
• queryXpath($xPathQuery): query the document using XPath notation.

85.2.2 ZendDomNodeList
As mentioned previously, ZendDomNodeList implements both Iterator and Countable, and as such can
be used in a foreach() loop as well as with the count() function. Additionally, it exposes the following methods:
• getCssQuery(): return the CSS selector query used to produce the result (if any).
• getXpathQuery(): return the XPath query used to produce the result. Internally, ZendDomQuery
converts CSS selector queries to XPath, so this value will always be populated.
• getDocument(): retrieve the DOMDocument the selection was made against.

85.2. Methods Available

369
Zend Framework 2 Documentation, Release 2.2.5

370

Chapter 85. ZendDomQuery
CHAPTER 86

Introduction to ZendEscaper

The OWASP Top 10 web security risks study lists Cross-Site Scripting (XSS) in second place. PHP’s sole functionality
against XSS is limited to two functions of which one is commonly misapplied. Thus, the ZendEscaper component
was written. It offers developers a way to escape output and defend from XSS and related vulnerabilities by introducing
contextual escaping based on peer-reviewed rules.
ZendEscaper was written with ease of use in mind, so it can be used completely stand-alone from the rest of the
framework, and as such can be installed with Composer.
For easier use of the Escaper component within the framework itself, especially with the ZendView component, a
set of view helpers is provided.
Warning: The ZendEscaper is a security related component. As such, if you believe you found an issue
with this component, we ask that you follow our Security Policy and report security issues accordingly. The Zend
Framework team and the contributors thanks you in advance.

86.1 Overview
The ZendEscaper component provides one class, ZendEscaperEscaper which in turn, provides five methods for escaping output. Which method to use when, depends on the context in which the outputted data is used. It is
up to the developer to use the right methods in the right context.
ZendEscaperEscaper has the following escaping methods available for each context:
• escapeHtml: escape a string for the HTML Body context.
• escapeHtmlAttr: escape a string for the HTML Attribute context.
• escapeJs: escape a string for the Javascript context.
• escapeCss: escape a string for the CSS context.
• escapeUrl: escape a string for the URI or Parameter contexts.
Usage of each method will be discussed in detail in later chapters.

371
Zend Framework 2 Documentation, Release 2.2.5

86.2 What ZendEscaper is not
ZendEscaper is meant to be used only for escaping data that is to be output, and as such should not be misused
for filtering input data. For such tasks, the ZendFilter component, HTMLPurifier or PHP’s Filter component should
be used.

372

Chapter 86. Introduction to ZendEscaper
CHAPTER 87

Theory of Operation

ZendEscaper provides methods for escaping output data, dependent on the context in which the data will be used.
Each method is based on peer-reviewed rules and is in compliance with the current OWASP recommendations.
The escaping follows a well known and fixed set of encoding rules for each key HTML context, which are defined by
OWASP. These rules cannot be impacted or negated by browser quirks or edge-case HTML parsing unless the browser
suffers a catastrophic bug in it’s HTML parser or Javascript interpreter - both of these are unlikely.
The contexts in which ZendEscaper should be used are HTML Body, HTML Attribute, Javascript, CSS and
URL/URI contexts.
Every escaper method will take the data to be escaped, make sure it is utf-8 encoded data, or try to convert it to utf-8,
do the context-based escaping, encode the escaped data back to it’s original encoding and return the data to the caller.
The actual escaping of the data differs between each method, they all have their own set of rules according to which
the escaping is done. An example will allow us to clearly demonstrate the difference, and how the same characters are
being escaped differently between contexts:
1

$escaper = new ZendEscaperEscaper(’utf-8’);

2
3
4
5
6
7
8
9
10
11
12

// &lt;script&gt;alert(&quot;zf2&quot;)&lt;/script&gt;
echo $escaper->escapeHtml(’<script>alert("zf2")</script>’);
// &lt;script&gt;alert&#x28;&quot;zf2&quot;&#x29;&lt;&#x2F;script&gt;
echo $escaper->escapeHtmlAttr(’<script>alert("zf2")</script>’);
// x3Cscriptx3Ealertx28x22zf2x22x29x3Cx2Fscriptx3E
echo $escaper->escapeJs(’<script>alert("zf2")</script>’);
// 3C script3E alert28 22 zf222 29 3C 2F script3E
echo $escaper->escapeCss(’<script>alert("zf2")</script>’);
// %3Cscript%3Ealert%28%22zf2%22%29%3C%2Fscript%3E
echo $escaper->escapeUrl(’<script>alert("zf2")</script>’);

More detailed examples will be given in later chapters.

87.1 The Problem with Inconsistent Functionality
At present, programmers orient towards the following PHP functions for each common HTML context:
• HTML Body: htmlspecialchars() or htmlentities()
• HTML Attribute: htmlspecialchars() or htmlentities()

373
Zend Framework 2 Documentation, Release 2.2.5

• Javascript: addslashes() or json_encode()
• CSS: n/a
• URL/URI: rawurlencode() or urlencode()
In practice, these decisions appear to depend more on what PHP offers, and if it can be interpreted as offering sufficient
escaping safety, than it does on what is recommended in reality to defend against XSS. While these functions can
prevent some forms of XSS, they do not cover all use cases or risks and are therefore insufficient defenses.
Using htmlspecialchars() in a perfectly valid HTML5 unquoted attribute value, for example, is completely useless
since the value can be terminated by a space (among other things) which is never escaped. Thus, in this instance, we
have a conflict between a widely used HTML escaper and a modern HTML specification, with no specific function
available to cover this use case. While it’s tempting to blame users, or the HTML specification authors, escaping just
needs to deal with whatever HTML and browsers allow.
Using addslashes(), custom backslash escaping or json_encode() will typically ignore HTML special characters such
as ampersands which may be used to inject entities into Javascript. Under the right circumstances, browser will convert
these entities into their literal equivalents before interpreting Javascript thus allowing attackers to inject arbitrary code.
Inconsistencies with valid HTML, insecure default parameters, lack of character encoding awareness, and misrepresentations of what functions are capable of by some programmers - these all make escaping in PHP an unnecessarily
convoluted quest.
To circumvent the lack of escaping methods in PHP, ZendEscaper addresses the need to apply context-specific
escaping in web applications. It implements methods that specifically target XSS and offers programmers a tool to
secure their applications without misusing other inadequate methods, or using, most likely incomplete, home-grown
solutions.

87.2 Why Contextual Escaping?
To understand why multiple standardised escaping methods are needed, here’s a couple of quick points (by no means
a complete set!):

87.2.1 HTML escaping of unquoted HTML attribute values still allows XSS
This is probably the best known way to defeat htmlspecialchars() when used on attribute values since any space (or
character interpreted as a space - there are a lot) lets you inject new attributes whose content can’t be neutralised by
HTML escaping. The solution (where this is possible) is additional escaping as defined by the OWASP ESAPI codecs.
The point here can be extended further - escaping only works if a programmer or designer know what they’re doing. In
many contexts, there are additional practices and gotchas that need to be carefully monitored since escaping sometimes
needs a little extra help to protect against XSS - even if that means ensuring all attribute values are properly double
quoted despite this not being required for valid HTML.

87.2.2 HTML escaping of CSS, Javascript or URIs is often reversed when passed
to non-HTML interpreters by the browser
HTML escaping is just that - it’s designed to escape a string for HTML (i.e. prevent tag or attribute insertion) but
not alter the underlying meaning of the content whether it be Text, Javascript, CSS or URIs. For that purpose a fully
HTML escaped version of any other context may still have its unescaped form extracted before it’s interpreted or
executed. For this reason we need separate escapers for Javascript, CSS and URIs and those writing templates must
know which escaper to apply to which context. Of course this means you need to be able to identify the correct context
before selecting the right escaper!

374

Chapter 87. Theory of Operation
Zend Framework 2 Documentation, Release 2.2.5

87.2.3 DOM based XSS requires a defence using at least two levels of different
escaping in many cases
DOM based XSS has become increasingly common as Javascript has taken off in popularity for large scale client side
coding. A simple example is Javascript defined in a template which inserts a new piece of HTML text into the DOM.
If the string is only HTML escaped, it may still contain Javascript that will execute in that context. If the string is
only Javascript escaped, it may contain HTML markup (new tags and attributes) which will be injected into the DOM
and parsed once the inserting Javascript executes. Damned either way? The solution is to escape twice - first escape
the string for HTML (make it safe for DOM insertion), and then for Javascript (make it safe for the current Javascript
context). Nested contexts are a common means of bypassing naive escaping habits (e.g. you can inject Javascript into
a CSS expression within a HTML Attribute).

87.2.4 PHP has no known anti-XSS escape functions (only those kidnapped from
their original purposes)
A simple example, widely used, is when you see json_encode() used to escape Javascript, or worse, some kind
of mutant addslashes() implementation. These were never designed to eliminate XSS yet PHP programmers use
them as such. For example, json_encode() does not escape the ampersand or semi-colon characters by default.
That means you can easily inject HTML entities which could then be decoded before the Javascript is evaluated in a
HTML document. This lets you break out of strings, add new JS statements, close tags, etc. In other words, using
json_encode() is insufficient and naive. The same, arguably, could be said for htmlspecialchars() which
has its own well known limitations that make a singular reliance on it a questionable practice.

87.2. Why Contextual Escaping?

375
Zend Framework 2 Documentation, Release 2.2.5

376

Chapter 87. Theory of Operation
CHAPTER 88

Configuring ZendEscaper

ZendEscaperEscaper has only one configuration option available, and that is the encoding to be used by the
Escaper object.
The default encoding is utf-8. Other supported encodings are:
• iso-8859-1
• iso-8859-5
• iso-8859-15
• cp866, ibm866, 866
• cp1251, windows-1251
• cp1252, windows-1252
• koi8-r, koi8-ru
• big5, big5-hkscs, 950, gb2312, 936
• shift_jis, sjis, sjis-win, cp932
• eucjp, eucjp-win
• macroman
If
an
unsupported
encoding
is
passed
to
ZendEscaperEscaper,
ZendEscaperExceptionInvalidArgumentException will be thrown.

a

377
Zend Framework 2 Documentation, Release 2.2.5

378

Chapter 88. Configuring ZendEscaper
CHAPTER 89

Escaping HTML

Probably the most common escaping happens in the HTML Body context. There are very few characters with special
meaning in this context, yet it is quite common to escape data incorrectly, namely by setting the wrong flags and
character encoding.
For escaping data in the HTML Body context, use ZendEscaperEscaper‘s escapeHtml method. Internally
it uses PHP’s htmlspecialchars, and additionally correctly sets the flags and encoding.
1
2

// outputting this without escaping would be a bad idea!
$input = ’<script>alert("zf2")</script>’;

3
4

$escaper = new ZendEscaperEscaper(’utf-8’);

5
6
7
8
9
10
11

// somewhere in an HTML template
<div class="user-provided-input">
<?php
echo $escaper->escapeHtml($input); // all safe!
?>
</div>

One thing a developer needs to pay special attention too, is that the encoding in which the document is served to the
client, as it must be the same as the encoding used for escaping!

89.1 Examples of Bad HTML Escaping
An example of incorrect usage:
1
2
3
4
5
6
7
8
9
10
11
12

<?php
$input = ’<script>alert("zf2")</script>’;
$escaper = new ZendEscaperEscaper(’utf-8’);
?>
<?php header(’Content-Type: text/html; charset=ISO-8859-1’); ?>
<!DOCTYPE html>
<html>
<head>
<title>Encodings set incorrectly!</title>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
</head>
<body>

379
Zend Framework 2 Documentation, Release 2.2.5

13
14
15
16
17

<?php
// Bad! The escaper’s and the document’s encodings are different!
echo $escaper->escapeHtml($input);
?>
</body>

89.2 Examples of Good HTML Escaping
An example of correct usage:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

<?php
$input = ’<script>alert("zf2")</script>’;
$escaper = new ZendEscaperEscaper(’utf-8’);
?>
<?php header(’Content-Type: text/html; charset=UTF-8’); ?>
<!DOCTYPE html>
<html>
<head>
<title>Encodings set correctly!</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<?php
// Good! The escaper’s and the document’s encodings are same!
echo $escaper->escapeHtml($input);
?>
</body>

380

Chapter 89. Escaping HTML
CHAPTER 90

Escaping HTML Attributes

Escaping data in the HTML Attribute context is most often done incorrectly, if not overlooked completely by developers. Regular HTML escaping can be used for escaping HTML attributes, but only if the attribute value can
be guaranteed as being properly quoted! To avoid confusion, we recommend always using the HTML Attribute
escaper method in the HTML Attribute context.
To escape data in the HTML Attribute, use ZendEscaperEscaper‘s escapeHtmlAttr method. Internally
it will convert the data to UTF-8, check for it’s validity, and use an extended set of characters to escape that are not
covered by htmlspecialchars to cover the cases where an attribute might be unquoted or quoted illegally.

90.1 Examples of Bad HTML Attribute Escaping
An example of incorrect HTML attribute escaping:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

<?php header(’Content-Type: text/html; charset=UTF-8’); ?>
<!DOCTYPE html>
<?php
$input = <<<INPUT
’ onmouseover=’alert(/ZF2!/);
INPUT;
/**
* NOTE: This is equivalent to using htmlspecialchars($input, ENT_COMPAT)
*/
$output = htmlspecialchars($input);
?>
<html>
<head>
<title>Single Quoted Attribute</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<div>
<?php
// the span tag will look like:
// <span title=’’ onmouseover=’alert(/ZF2!/);’>
?>
<span title=’<?php echo $output ?>’>
What framework are you using?

381
Zend Framework 2 Documentation, Release 2.2.5

25
26
27
28

</span>
</div>
</body>
</html>

In the above example, the default ENT_COMPAT flag is being used, which does not escape single quotes, thus resulting
in an alert box popping up when the onmouseover event happens on the span element.
Another example of incorrect HTML attribute escaping can happen when unquoted attributes are used, which is, by
the way, perfectly valid HTML5:
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

<?php header(’Content-Type: text/html; charset=UTF-8’); ?>
<!DOCTYPE html>
<?php
$input = <<<INPUT
faketitle onmouseover=alert(/ZF2!/);
INPUT;
// Tough luck using proper flags when the title attribute is unquoted!
$output = htmlspecialchars($input,ENT_QUOTES);
?>
<html>
<head>
<title>Quoteless Attribute</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<div>
<?php
// the span tag will look like:
// <span title=faketitle onmouseover=alert(/ZF2!/);>
?>
<span title=<?php echo $output ?>>
What framework are you using?
</span>
</div>
</body>
</html>

The above example shows how it is easy to break out from unquoted attributes in HTML5.

90.2 Examples of Good HTML Attribute Escaping
Both of the previous examples can be avoided by simply using the escapeHtmlAttr method:
1
2
3
4
5
6
7
8
9
10
11
12
13

<?php header(’Content-Type: text/html; charset=UTF-8’); ?>
<!DOCTYPE html>
<?php
$input = <<<INPUT
faketitle onmouseover=alert(/ZF2!/);
INPUT;
$escaper = new ZendEscaperEscaper(’utf-8’);
$output = $escaper->escapeHtmlAttr($input);
?>
<html>
<head>
<title>Quoteless Attribute</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

382

Chapter 90. Escaping HTML Attributes
Zend Framework 2 Documentation, Release 2.2.5

14
15
16
17
18
19
20
21
22
23
24
25
26

</head>
<body>
<div>
<?php
// the span tag will look like:
// <span title=faketitle&#x20;onmouseover&#x3D;alert&#x28;&#x2F;ZF2&#x21;&#x2F;&#x29;&#x3B;>
?>
<span title=<?php echo $output ?>>
What framework are you using?
</span>
</div>
</body>
</html>

In the above example, the malicious input from the attacker becomes completely harmless as we used proper HTML
attribute escaping!

90.2. Examples of Good HTML Attribute Escaping

383
Zend Framework 2 Documentation, Release 2.2.5

384

Chapter 90. Escaping HTML Attributes
CHAPTER 91

Escaping Javascript

Javascript string literals in HTML are subject to significant restrictions particularly due to the potential for unquoted
attributes and any uncertainty as to whether Javascript will be viewed as being CDATA or PCDATA by the browser.
To eliminate any possible XSS vulnerabilities, Javascript escaping for HTML extends the escaping rules of both
ECMAScript and JSON to include any potentially dangerous character. Very similar to HTML attribute value escaping,
this means escaping everything except basic alphanumeric characters and the comma, period and underscore characters
as hexadecimal or unicode escapes.
Javascript escaping applies to all literal strings and digits. It is not possible to safely escape other Javascript markup.
To escape data in the Javascript context, use ZendEscaperEscaper‘s escapeJs method. An extended
set of characters are escaped beyond ECMAScript’s rules for Javascript literal string escaping in order to prevent
misinterpretation of Javascript as HTML leading to the injection of special characters and entities.

91.1 Examples of Bad Javascript Escaping
An example of incorrect Javascript escaping:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

<?php header(’Content-Type: application/xhtml+xml; charset=UTF-8’); ?>
<!DOCTYPE html>
<?php
$input = <<<INPUT
bar&quot;; alert(&quot;Meow!&quot;); var xss=&quot;true
INPUT;
$output = json_encode($input);
?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Unescaped Entities</title>
<meta charset="UTF-8"/>
<script type="text/javascript">
<?php
// this will result in
// var foo = "bar&quot;; alert(&quot;Meow!&quot;); var xss=&quot;true";
?>
var foo = <?php echo $output ?>;
</script>
</head>
<body>

385
Zend Framework 2 Documentation, Release 2.2.5

22
23
24

<p>json_encode() is not good for escaping javascript!</p>
</body>
</html>

The above example will show an alert popup box as soon as the page is loaded, because the data is not properly escaped
for the Javascript context.

91.2 Examples of Good Javascript Escaping
By using the escapeJs method in the Javascript context, such attacks can be prevented:
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

<?php header(’Content-Type: text/html; charset=UTF-8’); ?>
<!DOCTYPE html>
<?php
$input = <<<INPUT
bar&quot;; alert(&quot;Meow!&quot;); var xss=&quot;true
INPUT;
$escaper = new ZendEscaperEscaper(’utf-8’);
$output = $escaper->escapeJs($input);
?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Escaped Entities</title>
<meta charset="UTF-8"/>
<script type="text/javascript">
<?php
// this will look like
// var foo = barx26quotx3Bx3Bx20alertx28x26quotx3BMeowx21x26quotx3Bx29x3Bx20var
?>
var foo = <?php echo $output ?>;
</script>
</head>
<body>
<p>ZendEscaperEscaper::escapeJs() is good for escaping javascript!</p>
</body>
</html>

In the above example, the Javascript parser will most likely report a SyntaxError, but at least the targeted application remains safe from such attacks.

386

Chapter 91. Escaping Javascript
CHAPTER 92

Escaping Cascading Style Sheets

CSS is similar to Javascript for the same reasons. CSS escaping excludes only basic alphanumeric characters and
escapes all other characters into valid CSS hexadecimal escapes.

92.1 Examples of Bad CSS Escaping
In most cases developers forget to escape CSS completely:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

<?php header(’Content-Type: application/xhtml+xml; charset=UTF-8’); ?>
<!DOCTYPE html>
<?php
$input = <<<INPUT
body {
background-image: url(’http://example.com/foo.jpg?</style><script>alert(1)</script>’);
}
INPUT;
?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Unescaped CSS</title>
<meta charset="UTF-8"/>
<style>
<?php echo $input; ?>
</style>
</head>
<body>
<p>User controlled CSS needs to be properly escaped!</p>
</body>
</html>

In the above example, by failing to escape the user provided CSS, an attacker can execute an XSS attack fairly easily.

92.2 Examples of Good CSS Escaping
By using escapeCss method in the CSS context, such attacks can be prevented:

387
Zend Framework 2 Documentation, Release 2.2.5

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

<?php header(’Content-Type: application/xhtml+xml; charset=UTF-8’); ?>
<!DOCTYPE html>
<?php
$input = <<<INPUT
body {
background-image: url(’http://example.com/foo.jpg?</style><script>alert(1)</script>’);
}
INPUT;
$escaper = new ZendEscaperEscaper(’utf-8’);
$output = $escaper->escapeCss($input);
?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Escaped CSS</title>
<meta charset="UTF-8"/>
<style>
<?php
// output will look something like
// body20 7B A 20 20 20 20 background2D image3A 20 url28 ...
echo $output;
?>
</style>
</head>
<body>
<p>User controlled CSS needs to be properly escaped!</p>
</body>
</html>

By properly escaping user controlled CSS, we can prevent XSS attacks in our web applications.

388

Chapter 92. Escaping Cascading Style Sheets
CHAPTER 93

Escaping URLs

This method is basically an alias for PHP’s rawurlencode() which has applied RFC 3986 since PHP 5.3. It is
included primarily for consistency.
URL escaping applies to data being inserted into a URL and not to the whole URL itself.

93.1 Examples of Bad URL Escaping
XSS attacks are easy if data inserted into URLs is not escaped properly:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

<?php header(’Content-Type: application/xhtml+xml; charset=UTF-8’); ?>
<!DOCTYPE html>
<?php
$input = <<<INPUT
" onmouseover="alert(’zf2’)
INPUT;
?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Unescaped URL data</title>
<meta charset="UTF-8"/>
</head>
<body>
<a href="http://example.com/?name=<?php echo $input; ?>">Click here!</a>
</body>
</html>

93.2 Examples of Good URL Escaping
By properly escaping data in URLs by using escapeUrl, we can prevent XSS attacks:
1
2
3
4
5
6

<?php header(’Content-Type: application/xhtml+xml; charset=UTF-8’); ?>
<!DOCTYPE html>
<?php
$input = <<<INPUT
" onmouseover="alert(’zf2’)
INPUT;

389
Zend Framework 2 Documentation, Release 2.2.5

7
8
9
10
11
12
13
14
15
16
17
18

$escaper = new ZendEscaperEscaper(’utf-8’);
$output = $escaper->escapeUrl($input);
?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Unescaped URL data</title>
<meta charset="UTF-8"/>
</head>
<body>
<a href="http://example.com/?name=<?php echo $output; ?>">Click here!</a>
</body>
</html>

390

Chapter 93. Escaping URLs
CHAPTER 94

The EventManager

94.1 Overview
The EventManager is a component designed for the following use cases:
• Implementing simple subject/observer patterns.
• Implementing Aspect-Oriented designs.
• Implementing event-driven architectures.
The basic architecture allows you to attach and detach listeners to named events, both on a per-instance basis as well
as via shared collections; trigger events; and interrupt execution of listeners.

94.2 Quick Start
Typically, you will compose an EventManager instance in a class.
1
2
3

use ZendEventManagerEventManagerInterface;
use ZendEventManagerEventManager;
use ZendEventManagerEventManagerAwareInterface;

4
5
6
7

class Foo implements EventManagerAwareInterface
{
protected $events;

8
9
10
11
12
13
14
15
16
17

public function setEventManager(EventManagerInterface $events)
{
$events->setIdentifiers(array(
__CLASS__,
get_called_class(),
));
$this->events = $events;
return $this;
}

18
19
20
21

public function getEventManager()
{
if (null === $this->events) {

391
Zend Framework 2 Documentation, Release 2.2.5

$this->setEventManager(new EventManager());
}
return $this->events;

22
23
24

}

25
26

}

The above allows users to access the EventManager instance, or reset it with a new instance; if one does not exist,
it will be lazily instantiated on-demand.
An EventManager is really only interesting if it triggers some events. Basic triggering takes three arguments: the
event name, which is usually the current function/method name; the “context”, which is usually the current object
instance; and the arguments, which are usually the arguments provided to the current function/method.
1
2
3

class Foo
{
// ... assume events definition from above

4

public function bar($baz, $bat = null)
{
$params = compact(’baz’, ’bat’);
$this->getEventManager()->trigger(__FUNCTION__, $this, $params);
}

5
6
7
8
9
10

}

In turn, triggering events is only interesting if something is listening for the event. Listeners attach to the
EventManager, specifying a named event and the callback to notify. The callback receives an Event object,
which has accessors for retrieving the event name, context, and parameters. Let’s add a listener, and trigger the event.
1

use ZendLogFactory as LogFactory;

2
3
4
5
6
7
8

$log = LogFactory($someConfig);
$foo = new Foo();
$foo->getEventManager()->attach(’bar’, function ($e) use ($log) {
$event = $e->getName();
$target = get_class($e->getTarget());
$params = json_encode($e->getParams());

9

$log->info(sprintf(
’%s called on %s, using params %s’,
$event,
$target,
$params
));

10
11
12
13
14
15
16

});

17
18
19
20

// Results in log message:
$foo->bar(’baz’, ’bat’);
// reading: bar called on Foo, using params {"baz" : "baz", "bat" : "bat"}"

Note that the second argument to attach() is any valid callback; an anonymous function is shown in the example
in order to keep the example self-contained. However, you could also utilize a valid function name, a functor, a string
referencing a static method, or an array callback with a named static method or instance method. Again, any PHP
callback is valid.
Sometimes you may want to specify listeners without yet having an object instance of the class composing an
EventManager. Zend Framework enables this through the concept of a SharedEventCollection. Simply
put, you can inject individual EventManager instances with a well-known SharedEventCollection, and the
EventManager instance will query it for additional listeners. Listeners attach to a SharedEventCollection
in roughly the same way the do normal event managers; the call to attach is identical to the EventManager,
392

Chapter 94. The EventManager
Zend Framework 2 Documentation, Release 2.2.5

but expects an additional parameter at the beginning: a named instance. Remember the example of composing an
EventManager, how we passed it __CLASS__? That value, or any strings you provide in an array to the constructor, may be used to identify an instance when using a SharedEventCollection. As an example, assuming
we have a SharedEventManager instance that we know has been injected in our EventManager instances (for
instance, via dependency injection), we could change the above example to attach via the shared collection:
1

use ZendLogFactory as LogFactory;

2
3

// Assume $events is a ZendEventManagerSharedEventManager instance

4
5
6
7
8
9

$log = LogFactory($someConfig);
$events->attach(’Foo’, ’bar’, function ($e) use ($log) {
$event = $e->getName();
$target = get_class($e->getTarget());
$params = json_encode($e->getParams());

10

$log->info(sprintf(
’%s called on %s, using params %s’,
$event,
$target,
$params
));

11
12
13
14
15
16
17

});

18
19
20
21

// Later, instantiate Foo:
$foo = new Foo();
$foo->getEventManager()->setSharedManager($events);

22
23
24
25
26

// And we can still trigger the above event:
$foo->bar(’baz’, ’bat’);
// results in log message:
// bar called on Foo, using params {"baz" : "baz", "bat" : "bat"}"

Note: StaticEventManager
As of 2.0.0beta3, you can use the StaticEventManager singleton as a SharedEventCollection. As such,
you do not need to worry about where and how to get access to the SharedEventCollection; it’s globally
available by simply calling StaticEventManager::getInstance().
Be aware, however, that its usage is deprecated within the framework, and starting with 2.0.0beta4, you will
instead configure a SharedEventManager instance that will be injected by the framework into individual
EventManager instances.
The EventManager also provides the ability to detach listeners, short-circuit execution of an event either from
within a listener or by testing return values of listeners, test and loop through the results returned by listeners, prioritize
listeners, and more. Many of these features are detailed in the examples.

94.2.1 Wildcard Listeners
Sometimes you’ll want to attach the same listener to many events or to all events of a given instance – or potentially,
with a shared event collection, many contexts, and many events. The EventManager component allows for this.

94.2. Quick Start

393
Zend Framework 2 Documentation, Release 2.2.5

Attaching to many events at once

1
2

$events = new EventManager();
$events->attach(array(’these’, ’are’, ’event’, ’names’), $callback);

Note that if you specify a priority, that priority will be used for all events specified.
Attaching using the wildcard

1
2

$events = new EventManager();
$events->attach(’*’, $callback);

Note that if you specify a priority, that priority will be used for this listener for any event triggered.
What the above specifies is that any event triggered will result in notification of this particular listener.
Attaching to many events at once via a SharedEventManager

1
2
3

$events = new SharedEventManager();
// Attach to many events on the context "foo"
$events->attach(’foo’, array(’these’, ’are’, ’event’, ’names’), $callback);

4
5
6

// Attach to many events on the contexts "foo" and "bar"
$events->attach(array(’foo’, ’bar’), array(’these’, ’are’, ’event’, ’names’), $callback);

Note that if you specify a priority, that priority will be used for all events specified.
Attaching using the wildcard via a SharedEventManager

1
2
3

$events = new SharedEventManager();
// Attach to all events on the context "foo"
$events->attach(’foo’, ’*’, $callback);

4
5
6

// Attach to all events on the contexts "foo" and "bar"
$events->attach(array(’foo’, ’bar’), ’*’, $callback);

Note that if you specify a priority, that priority will be used for all events specified.
The above is specifying that for the contexts “foo” and “bar”, the specified listener should be notified for any event
they trigger.

94.3 Configuration Options
EventManager Options

identifier A string or array of strings to which the given EventManager instance can answer when accessed via a
SharedEventManager.
event_class The name of an alternate Event class to use for representing events passed to listeners.
shared_collections An instance of a SharedEventCollection instance to use when triggering events.

394

Chapter 94. The EventManager
Zend Framework 2 Documentation, Release 2.2.5

94.4 Available Methods
__construct __construct(null|string|int $identifier)
Constructs a new EventManager instance, using the given identifier, if provided, for purposes of shared
collections.
setEventClass setEventClass(string $class)
Provide the name of an alternate Event class to use when creating events to pass to triggered listeners.
setSharedCollections setSharedCollections(SharedEventCollection $collections =
null)
An instance of a SharedEventCollection instance to use when triggering events.
getSharedCollections getSharedCollections()
Returns the currently attached SharedEventCollection instance. Returns either a null if no collection
is attached, or a SharedEventCollection instance otherwise.
trigger trigger(string $event, mixed $target = null, mixed $argv, callback
$callback = null)
Triggers all listeners to a named event. The recommendation is to use the current function/method name for
$event, appending it with values such as ”.pre”, ”.post”, etc. as needed. $target should be the current
object instance, or the name of the function if not triggering within an object. $argv should typically be an associative array or ArrayAccess instance; we recommend using the parameters passed to the function/method
(compact() is often useful here). This method can also take a callback and behave in the same way as
triggerUntil().
The method returns an instance of ResponseCollection, which may be used to introspect return values of
the various listeners, test for short-circuiting, and more.
triggerUntil triggerUntil(string $event, mixed $target, mixed $argv = null,
callback $callback = null)
Triggers all listeners to a named event, just like trigger(), with the addition that it passes the return value
from each listener to $callback; if $callback returns a boolean true value, execution of the listeners is
interrupted. You can test for this using $result->stopped().
attach attach(string $event, callback $callback, int $priority)
Attaches $callback to the EventManager instance, listening for the event $event. If a $priority is
provided, the listener will be inserted into the internal listener stack using that priority; higher values execute
earliest. (Default priority is “1”, and negative priorities are allowed.)
The method returns an instance of ZendStdlibCallbackHandler; this value can later be passed to
detach() if desired.
attachAggregate attachAggregate(string|ListenerAggregate $aggregate)
If a string is passed for $aggregate, instantiates that class. The $aggregate is then passed the
EventManager instance to its attach() method so that it may register listeners.
The ListenerAggregate instance is returned.
detach detach(CallbackHandler|ListenerAggregateInterface $listener)
Scans all listeners, and detaches any that match $listener so that they will no longer be triggered.
Returns a boolean true if any listeners have been identified and unsubscribed, and a boolean false otherwise.

94.4. Available Methods

395
Zend Framework 2 Documentation, Release 2.2.5

detachAggregate detachAggregate(ListenerAggregateInterface $aggregate)
Loops through all listeners of all events to identify listeners that are represented by the aggregate; for all matches,
the listeners will be removed.
Returns a boolean true if any listeners have been identified and unsubscribed, and a boolean false otherwise.
getEvents getEvents()
Returns an array of all event names that have listeners attached.
getListeners getListeners(string $event)
Returns a ZendStdlibPriorityQueue instance of all listeners attached to $event.
clearListeners clearListeners(string $event)
Removes all listeners attached to $event.
prepareArgs prepareArgs(array $args)
Creates an ArrayObject from the provided $args. This can be useful if you want yours listeners to be able
to modify arguments such that later listeners or the triggering method can see the changes.

94.5 Examples
Modifying Arguments

Occasionally it can be useful to allow listeners to modify the arguments they receive so that later listeners or the calling
method will receive those changed values.
As an example, you might want to pre-filter a date that you know will arrive as a string and convert it to a DateTime
argument.
To do this, you can pass your arguments to prepareArgs(), and pass this new object when triggering an event.
You will then pull that value back into your method.
1
2
3

class ValueObject
{
// assume a composed event manager

4

function inject(array $values)
{
$argv = compact(’values’);
$argv = $this->getEventManager()->prepareArgs($argv);
$this->getEventManager()->trigger(__FUNCTION__, $this, $argv);
$date = isset($argv[’values’][’date’]) ? $argv[’values’][’date’] : new DateTime(’now’);

5
6
7
8
9
10
11

// ...

12

}

13
14

}

15
16

$v = new ValueObject();

17
18
19
20
21
22
23

$v->getEventManager()->attach(’inject’, function($e) {
$values = $e->getParam(’values’);
if (!$values) {
return;
}
if (!isset($values[’date’])) {

396

Chapter 94. The EventManager
Zend Framework 2 Documentation, Release 2.2.5

$values[’date’] = new DateTime(’now’);
return;

24
25

}
$values[’date’] = new Datetime($values[’date’]);

26
27
28

});

29
30
31
32

$v->inject(array(
’date’ => ’2011-08-10 15:30:29’,
));

Short Circuiting

One common use case for events is to trigger listeners until either one indicates no further processing should be done,
or until a return value meets specific criteria. As examples, if an event creates a Response object, it may want execution
to stop.
1
2

$listener = function($e) {
// do some work

3

// Stop propagation and return a response
$e->stopPropagation(true);
return $response;

4
5
6
7

};

Alternately, we could do the check from the method triggering the event.
1
2
3

class Foo implements DispatchableInterface
{
// assume composed event manager

4

public function dispatch(Request $request, Response $response = null)
{
$argv = compact(’request’, ’response’);
$results = $this->getEventManager()->triggerUntil(__FUNCTION__, $this, $argv, function($v) {
return ($v instanceof Response);
});
}

5
6
7
8
9
10
11
12

}

Typically, you may want to return a value that stopped execution, or use it some way. Both trigger() and
triggerUntil() return a ResponseCollection instance; call its stopped() method to test if execution
was stopped, and last() method to retrieve the return value from the last executed listener:
1
2
3

class Foo implements DispatchableInterface
{
// assume composed event manager

4
5
6
7
8
9
10

public function dispatch(Request $request, Response $response = null)
{
$argv = compact(’request’, ’response’);
$results = $this->getEventManager()->triggerUntil(__FUNCTION__, $this, $argv, function($v) {
return ($v instanceof Response);
});

11
12
13
14

// Test if execution was halted, and return last result:
if ($results->stopped()) {
return $results->last();

94.5. Examples

397
Zend Framework 2 Documentation, Release 2.2.5

}

15
16

// continue...

17

}

18
19

}

Assigning Priority to Listeners

One use case for the EventManager is for implementing caching systems. As such, you often want to check the
cache early, and save to it late.
The third argument to attach() is a priority value. The higher this number, the earlier that listener will execute; the
lower it is, the later it executes. The value defaults to 1, and values will trigger in the order registered within a given
priority.
So, to implement a caching system, our method will need to trigger an event at method start as well as at method end.
At method start, we want an event that will trigger early; at method end, an event should trigger late.
Here is the class in which we want caching:
1
2
3

class SomeValueObject
{
// assume it composes an event manager

4

public function get($id)
{
$params = compact(’id’);
$results = $this->getEventManager()->trigger(’get.pre’, $this, $params);

5
6
7
8
9

// If an event stopped propagation, return the value
if ($results->stopped()) {
return $results->last();
}

10
11
12
13
14

// do some work...

15
16

$params[’__RESULT__’] = $someComputedContent;
$this->getEventManager()->trigger(’get.post’, $this, $params);

17
18

}

19
20

}

Now, let’s create a ListenerAggregateInterface that can handle caching for us:
1
2
3
4

use
use
use
use

ZendCacheCache;
ZendEventManagerEventManagerInterface;
ZendEventManagerListenerAggregateInterface;
ZendEventManagerEventInterface;

5
6
7
8

class CacheListener implements ListenerAggregateInterface
{
protected $cache;

9

protected $listeners = array();

10
11

public function __construct(Cache $cache)
{
$this->cache = $cache;
}

12
13
14
15

398

Chapter 94. The EventManager
Zend Framework 2 Documentation, Release 2.2.5

16

public function attach(EventManagerInterface $events)
{
$this->listeners[] = $events->attach(’get.pre’, array($this, ’load’), 100);
$this->listeners[] = $events->attach(’get.post’, array($this, ’save’), -100);
}

17
18
19
20
21
22

public function detach(EventManagerInterface $events)
{
foreach ($this->listeners as $index => $listener) {
if ($events->detach($listener)) {
unset($this->listeners[$index]);
}
}
}

23
24
25
26
27
28
29
30
31

public function load(EventInterface $e)
{
$id = get_class($e->getTarget()) . ’-’ . json_encode($e->getParams());
if (false !== ($content = $this->cache->load($id))) {
$e->stopPropagation(true);
return $content;
}
}

32
33
34
35
36
37
38
39
40

public function save(EventInterface $e)
{
$params = $e->getParams();
$content = $params[’__RESULT__’];
unset($params[’__RESULT__’]);

41
42
43
44
45
46

$id = get_class($e->getTarget()) . ’-’ . json_encode($params);
$this->cache->save($content, $id);

47
48

}

49
50

}

We can then attach the aggregate to an instance.
1
2
3

$value
= new SomeValueObject();
$cacheListener = new CacheListener($cache);
$value->getEventManager()->attachAggregate($cacheListener);

Now, as we call get(), if we have a cached entry, it will be returned immediately; if not, a computed entry will be
cached when we complete the method.

94.5. Examples

399
Zend Framework 2 Documentation, Release 2.2.5

400

Chapter 94. The EventManager
CHAPTER 95

Introduction to ZendFeed

ZendFeed provides functionality for consuming RSS and Atom feeds. It provides a natural syntax for accessing
elements of feeds, feed attributes, and entry attributes. ZendFeed also has extensive support for modifying feed
and entry structure with the same natural syntax, and turning the result back into XML. In the future, this modification
support could provide support for the Atom Publishing Protocol.
ZendFeed consists of ZendFeedReader for reading RSS and Atom feeds, ZendFeedWriter for writing RSS and Atom feeds, and ZendFeedPubSubHubbub for working with Hub servers. Furthermore, both
ZendFeedReader and ZendFeedWriter support extensions which allows for working with additional
data in feeds, not covered in the core API but used in conjunction with RSS and Atom feeds.
In the example below, we demonstrate a simple use case of retrieving an RSS feed and saving relevant portions of the
feed data to a simple PHP array, which could then be used for printing the data, storing to a database, etc.
Note: Be aware
Many RSS feeds have different channel and item properties available. The RSS specification provides for many optional
properties, so be aware of this when writing code to work with RSS data. ZendFeed supports all optional properties
of the core RSS and Atom specifications.

95.1 Reading RSS Feed Data with ZendFeedReader
1
2
3
4
5
6
7
8
9

// Fetch the latest Slashdot headlines
try {
$slashdotRss =
ZendFeedReaderReader::import(’http://rss.slashdot.org/Slashdot/slashdot’);
} catch (ZendFeedReaderExceptionRuntimeException $e) {
// feed import failed
echo "Exception caught importing feed: {$e->getMessage()}n";
exit;
}

10
11
12
13
14
15

// Initialize the
$channel = array(
’title’
’link’
’description’

channel/feed data array
=> $slashdotRss->getTitle(),
=> $slashdotRss->getLink(),
=> $slashdotRss->getDescription(),

401
Zend Framework 2 Documentation, Release 2.2.5

’items’
);

16
17

=> array()

18
19
20
21
22
23
24
25
26

// Loop over each channel item/entry and store relevant data for each
foreach ($slashdotRss as $item) {
$channel[’items’][] = array(
’title’
=> $item->getTitle(),
’link’
=> $item->getLink(),
’description’ => $item->getDescription()
);
}

Your $channel array now contains the basic meta-information for the RSS channel and all items that it contained.
The process is identical for Atom feeds since ZendFeed features a common denominator API, i.e. all getters and
setters are the same regardless of feed format.

402

Chapter 95. Introduction to ZendFeed
CHAPTER 96

Importing Feeds

ZendFeed enables developers to retrieve feeds very easily, by using ZendFeaderReader. If you know the
URI of a feed, simply use the ZendFeedReaderReader::import() method:
1

$feed = ZendFeedReaderReader::import(’http://feeds.example.com/feedName’);

You can also use ZendFeedReaderReader to fetch the contents of a feed from a file or the contents of a PHP
string variable:
1
2

// importing a feed from a text file
$feedFromFile = ZendFeedReaderReader::importFile(’feed.xml’);

3
4
5

// importing a feed from a PHP string variable
$feedFromPHP = ZendFeedReaderReader::importString($feedString);

In each of the examples above, an object of a class that extends ZendFeedReaderFeedAbstractFeed
is returned upon success, depending on the type of the feed. If an RSS feed were retrieved via one of the import
methods above, then a ZendFeedReaderFeedRss object would be returned. On the other hand, if an Atom
feed were imported, then a ZendFeedReaderFeedAtom object is returned. The import methods will also
throw a ZendFeedExceptionReaderRuntimeException object upon failure, such as an unreadable or
malformed feed.

96.1 Dumping the contents of a feed
To dump the contents of a ZendFeedReaderFeedAbstractFeed instance, you may use the saveXml()
method.
1

assert($feed instanceof ZendFeedReaderFeedAbstractFeed);

2
3
4

// dump the feed to standard output
print $feed->saveXml();

403
Zend Framework 2 Documentation, Release 2.2.5

404

Chapter 96. Importing Feeds
CHAPTER 97

Retrieving Feeds from Web Pages

97.1 Find Feed Links
Web pages often contain <link> tags that refer to feeds with content relevant to the particular page.
ZendFeedReaderReader enables you to retrieve all feeds referenced by a web page with one simple method
call:
1

$feedLinks = ZendFeedReaderReader::findFeedLinks(’http://www.example.com/news.html’);

Here the findFeedLinks() method returns a ZendFeedReaderFeedSet object, that
is in turn, a collection of other ZendFeedReaderFeedSet objects, that are referenced
by <link> tags on the news.html web page.
ZendFeedReaderReader will throw a
ZendFeedReaderExceptionRuntimeException upon failure, such as an HTTP 404 response
code or a malformed feed.
You can examine all feed links located by iterating across the collection:
1
2
3
4
5
6
7

$rssFeed = null;
$feedLinks = ZendFeedReaderReader::findFeedLinks(’http://www.example.com/news.html’);
foreach ($feedLinks as $link) {
if (stripos($link[’type’], ’application/rss+xml’) !== false) {
$rssFeed = $link[’href’];
break;
}

Each ZendFeedReaderFeedSet object will expose the rel, href, type and title properties of detected links for
all RSS, Atom or RDF feeds. You can always select the first encountered link of each type by using a shortcut:
1
2
3

$rssFeed = null;
$feedLinks = ZendFeedReaderReader::findFeedLinks(’http://www.example.com/news.html’);
$firstAtomFeed = $feedLinks->atom;

405
Zend Framework 2 Documentation, Release 2.2.5

406

Chapter 97. Retrieving Feeds from Web Pages
CHAPTER 98

Consuming an RSS Feed

98.1 Reading a feed
Reading an RSS feed is as simple as passing the URL of the feed to ZendFeedReaderReader‘s import
method.
1

$channel = ZendFeedReaderReader::import(’http://rss.example.com/channelName’);

If any errors occur fetching the feed, a ZendFeedReaderExceptionRuntimeException will be
thrown.

98.2 Get properties
Once you have a feed object, you can access any of the standard RSS “channel” properties directly on the object:
1

echo $channel->getTitle();

Properties of the channel can be accessed via getter methods, such as getTitle, getAuthor ...
If channel properties have attributes, the getter method will return a key/value pair, where the key is the attribute name,
and the value is the attribute value.
1
2

$author = $channel->getAuthor();
echo $author[’name’];

Most commonly you’ll want to loop through the feed and do something with its entries.
ZendFeedReaderFeedRss internally converts all entries to a ZendFeedReaderEntryRss.
Entry properties, similarly to channel properties, can be accessed via getter methods, such as getTitle,
getDescription ...
An example of printing all titles of articles in a channel is:
1
2
3

foreach ($channel as $item) {
echo $item->getTitle() . "n";
}

If you are not familiar with RSS, here are the standard elements you can expect to be available in an RSS channel and
in individual RSS items (entries).

407
Zend Framework 2 Documentation, Release 2.2.5

Required channel elements:
• title- The name of the channel
• link- The URL of the web site corresponding to the channel
• description- A sentence or several describing the channel
Common optional channel elements:
• pubDate- The publication date of this set of content, in RFC 822 date format
• language- The language the channel is written in
• category- One or more (specified by multiple tags) categories the channel belongs to
RSS <item> elements do not have any strictly required elements. However, either title or description must be
present.
Common item elements:
• title- The title of the item
• link- The URL of the item
• description- A synopsis of the item
• author- The author’s email address
• category- One more categories that the item belongs to
• comments-URL of comments relating to this item
• pubDate- The date the item was published, in RFC 822 date format
In your code you can always test to see if an element is non-empty with:
1
2
3

if ($item->getPropname()) {
// ... proceed.
}

Where relevant, ZendFeed supports a number of common RSS extensions including Dublin Core, Atom (inside
RSS) and the Content, Slash, Syndication, Syndication/Thread and several other extensions or modules.
For further information, the official RSS 2.0 specification is available at: http://blogs.law.harvard.edu/tech/rss

408

Chapter 98. Consuming an RSS Feed
CHAPTER 99

Consuming an Atom Feed

ZendFeedReaderFeedAtom is used in much the same way as ZendFeedReaderFeedRss. It
provides the same access to feed-level properties and iteration over entries in the feed. The main difference is in the
structure of the Atom protocol itself. Atom is a successor to RSS; it is a more generalized protocol and it is designed
to deal more easily with feeds that provide their full content inside the feed, splitting RSS‘ description tag into
two elements, summary and content, for that purpose.

99.1 Basic Use of an Atom Feed
Read an Atom feed and print the title and summary of each entry:
1
2
3
4
5
6
7

$feed = ZendFeedReaderReader::import(’http://atom.example.com/feed/’);
echo ’The feed contains ’ . $feed->count() . ’ entries.’ . "nn";
foreach ($feed as $entry) {
echo ’Title: ’ . $entry->getTitle() . "n";
echo ’Description: ’ . $entry->getDescription() . "n";
echo ’URL: ’ . $entry->getLink() . "nn";
}

In an Atom feed you can expect to find the following feed properties:
• title- The feed’s title, same as RSS‘s channel title
• id- Every feed and entry in Atom has a unique identifier
• link- Feeds can have multiple links, which are distinguished by a type attribute
The equivalent to RSS‘s channel link would be type="text/html". if the link is to an alternate version of
the same content that’s in the feed, it would have a rel="alternate" attribute.
• subtitle- The feed’s description, equivalent to RSS‘ channel description
• author- The feed’s author, with name and email sub-tags
Atom entries commonly have the following properties:
• id- The entry’s unique identifier
• title- The entry’s title, same as RSS item titles
• link- A link to another format or an alternate view of this entry

409
Zend Framework 2 Documentation, Release 2.2.5

The link property of an atom entry typically has an href attribute.
• summary- A summary of this entry’s content
• content- The full content of the entry; can be skipped if the feed just contains summaries
• author- with name and email sub-tags like feeds have
• published- the date the entry was published, in RFC 3339 format
• updated- the date the entry was last updated, in RFC 3339 format
Where relevant, ZendFeed supports a number of common RSS extensions including Dublin Core and the Content,
Slash, Syndication, Syndication/Thread and several other extensions in common use on blogs.
For more information on Atom and plenty of resources, see http://www.atomenabled.org/.

410

Chapter 99. Consuming an Atom Feed
CHAPTER 100

Consuming a Single Atom Entry

Single Atom <entry> elements are also valid by themselves. Usually the URL for an entry is the feed’s URL followed
by /<entryId>, such as http://atom.example.com/feed/1, using the example URL we used above. This
pattern may exist for some web services which use Atom as a container syntax.
If you read a single entry, you will have a ZendFeedReaderEntryAtom object.

100.1 Reading a Single-Entry Atom Feed
1
2

$entry = ZendFeedReaderReader::import(’http://atom.example.com/feed/1’);
echo ’Entry title: ’ . $entry->getTitle();

411
Zend Framework 2 Documentation, Release 2.2.5

412

Chapter 100. Consuming a Single Atom Entry
CHAPTER 101

ZendFeed and Security

101.1 Introduction
As with any data coming from a source that is beyond the developer’s control, special attention needs to be given to
securing, validating and filtering that data. Similar to data input to our application by users, data coming from RSS
and Atom feeds should also be considered unsafe and potentially dangerous, as it allows the delivery of HTML and
xHTML 1 . Because data validation and filtration is out of ZendFeed‘s scope, this task is left for implementation by
the developer, by using libraries such as ZendEscaper for escaping and HTMLPurifier for validating and filtering
feed data.
Escaping and filtering of potentially insecure data is highly recommended before outputting it anywhere in our application or before storing that data in some storage engine (be it a simple file, a database...).

101.2 Filtering data using HTMLPurifier
Currently the best available library for filtering and validating (x)HTML data in PHP is HTMLPurifier and, as such, is
the recommended tool for this task. HTMLPurifier works by filtering out all (x)HTML from the data, except for the
tags and attributes specifically allowed in a whitelist, and by checking and fixing nesting of tags, ensuring a standardscompliant output.
The following examples will show a basic usage of HTMLPurifier, but developers are urged to go through and read
HTMLPurifier’s documentation.
1
2
3
4
5
6
7
8
9
10
11
12

// Setting HTMLPurifier’s options
$options = array(
// Allow only paragraph tags
// and anchor tags wit the href attribute
array(
’HTML.Allowed’,
’p,a[href]’
),
// Format end output with Tidy
array(
’Output.TidyFormat’,
true
1

http://tools.ietf.org/html/rfc4287#section-8.1

413
Zend Framework 2 Documentation, Release 2.2.5

),
// Assume XHTML 1.0 Strict Doctype
array(
’HTML.Doctype’,
’XHTML 1.0 Strict’
),
// Disable cache, but see note after the example
array(
’Cache.DefinitionImpl’,
null
)

13
14
15
16
17
18
19
20
21
22
23
24

);

25
26
27
28
29
30

// Configuring HTMLPurifier
$config = HTMLPurifier_Config::createDefault();
foreach ($options as $option) {
$config->set($option[0], $option[1]);
}

31
32
33

// Creating a HTMLPurifier with it’s config
$purifier = new HTMLPurifier($config);

34
35
36
37
38
39
40
41
42

// Fetch the RSS
try {
$rss = ZendFeedReaderReader::import(’http://www.planet-php.net/rss/’);
} catch (ZendFeedExceptionReaderRuntimeException $e) {
// feed import failed
echo "Exception caught importing feed: {$e->getMessage()}n";
exit;
}

43
44
45
46
47
48
49
50
51

// Initialize the channel data array
// See that we’re cleaning the description with HTMLPurifier
$channel = array(
’title’
=> $rss->getTitle(),
’link’
=> $rss->getLink(),
’description’ => $purifier->purify($rss->getDescription()),
’items’
=> array()
);

52
53
54
55
56
57
58
59
60
61

// Loop over each channel item and store relevant data
// See that we’re cleaning the descriptions with HTMLPurifier
foreach ($rss as $item) {
$channel[’items’][] = array(
’title’
=> $item->getTitle(),
’link’
=> $item->getLink(),
’description’ => $purifier->purify($item->getDescription())
);
}

Note: HTMLPurifier is using the PHP Tidy extension to clean and repair the final output. If this extension is not
available, it will silently fail but its availability has no impact on the library’s security.

Note: For the sake of this example, the HTMLPurifier’s cache is disabled, but it is recommended to configure caching
and use its standalone include file as it can improve the performance of HTMLPurifier substantially.

414

Chapter 101. ZendFeed and Security
Zend Framework 2 Documentation, Release 2.2.5

101.3 Escaping data using ZendEscaper
To help prevent XSS attacks, Zend Framework has a new component ZendEscaper, which complies to the current
OWASP recommendations, and as such, is the recommended tool for escaping HTML tags and attributes, Javascript,
CSS and URLs before outputing any potentially insecure data to the users.
1
2
3
4
5
6
7

try {
$rss = ZendFeedReaderReader::import(’http://www.planet-php.net/rss/’);
} catch (ZendFeedExceptionReaderRuntimeException $e) {
// feed import failed
echo "Exception caught importing feed: {$e->getMessage()}n";
exit;
}

8
9
10
11
12
13
14

// Validate all URIs
$linkValidator = new ZendValidatorUri;
$link = null;
if ($linkValidator->isValid($rss->getLink())) {
$link = $rss->getLink();
}

15
16
17

// Escaper used for escaping data
$escaper = new ZendEscaperEscaper(’utf-8’);

18
19
20
21
22
23
24
25

// Initialize the
$channel = array(
’title’
’link’
’description’
’items’
);

channel data array
=>
=>
=>
=>

$escaper->escapeHtml($rss->getTitle()),
$escaper->escapeHtml($link),
$escaper->escapeHtml($rss->getDescription()),
array()

26
27
28
29
30
31
32
33
34
35
36
37
38

// Loop over each channel item and store relevant data
foreach ($rss as $item) {
$link = null;
if ($linkValidator->isValid($rss->getLink())) {
$link = $item->getLink();
}
$channel[’items’][] = array(
’title’
=> $escaper->escapeHtml($item->getTitle()),
’link’
=> $escaper->escapeHtml($link),
’description’ => $escaper->escapeHtml($item->getDescription())
);
}

The feed data is now safe for output to HTML templates. You can, of course, skip escaping when simply storing the
data persistently but remember to escape it on output later!
Of course, these are just basic examples, and cannot cover all possible scenarios that you, as a developer, can, and
most likely will, encounter. Your responsibility is to learn what libraries and tools are at your disposal, and when and
how to use them to secure your web applications.

101.3. Escaping data using ZendEscaper

415
Zend Framework 2 Documentation, Release 2.2.5

416

Chapter 101. ZendFeed and Security
CHAPTER 102

ZendFeedReaderReader

102.1 Introduction
ZendFeedReaderReader is a component used to consume RSS and Atom feeds of any version, including RDF/RSS 1.0, RSS 2.0, Atom 0.3 and Atom 1.0. The API for retrieving feed data is deliberately simple since
ZendFeedReader is capable of searching any feed of any type for the information requested through the API. If
the typical elements containing this information are not present, it will adapt and fall back on a variety of alternative
elements instead. This ability to choose from alternatives removes the need for users to create their own abstraction
layer on top of the component to make it useful or have any in-depth knowledge of the underlying standards, current
alternatives, and namespaced extensions.
Internally, ZendFeedReaderReader works almost entirely on the basis of making XPath queries against the
feed XML‘s Document Object Model. This singular approach to parsing is consistent and the component offers a
plugin system to add to the Feed and Entry level API by writing Extensions on a similar basis.
Performance is assisted in three ways. First of all, ZendFeedReaderReader supports caching using
ZendCache to maintain a copy of the original feed XML. This allows you to skip network requests for a feed
URI if the cache is valid. Second, the Feed and Entry level API is backed by an internal cache (non-persistent) so
repeat API calls for the same feed will avoid additional DOM or XPath use. Thirdly, importing feeds from a URI
can take advantage of HTTP Conditional GET requests which allow servers to issue an empty 304 response when the
requested feed has not changed since the last time you requested it. In the final case, an instance of ZendCache
will hold the last received feed along with the ETag and Last-Modified header values sent in the HTTP response.
ZendFeedReaderReader is not capable of constructing feeds and delegates this responsibility to
ZendFeedWriterWriter.

102.2 Importing Feeds
Feeds can be imported from a string, file or an URI. Importing from a URI can additionally utilise a
HTTP Conditional GET request. If importing fails, an exception will be raised. The end result will be
an object of type ZendFeedReaderFeedAbstractFeed, the core implementations of which are
ZendFeedReaderFeedRss and ZendFeedReaderFeedAtom. Both objects support multiple (all
existing) versions of these broad feed types.
In the following example, we import an RDF/RSS 1.0 feed and extract some basic information that can be saved to a
database or elsewhere.

417
Zend Framework 2 Documentation, Release 2.2.5

1
2
3
4
5
6
7
8
9

$feed = ZendFeedReaderReader::import(’http://www.planet-php.net/rdf/’);
$data = array(
’title’
=> $feed->getTitle(),
’link’
=> $feed->getLink(),
’dateModified’ => $feed->getDateModified(),
’description’ => $feed->getDescription(),
’language’
=> $feed->getLanguage(),
’entries’
=> array(),
);

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

foreach ($feed as $entry) {
$edata = array(
’title’
=> $entry->getTitle(),
’description’ => $entry->getDescription(),
’dateModified’ => $entry->getDateModified(),
’authors’
=> $entry->getAuthors(),
’link’
=> $entry->getLink(),
’content’
=> $entry->getContent()
);
$data[’entries’][] = $edata;
}

The example above demonstrates ZendFeedReaderReader‘s API, and it also demonstrates some of its internal operation. In reality, the RDF feed selected does not have any native date or author elements, however it does utilise
the Dublin Core 1.1 module which offers namespaced creator and date elements. ZendFeedReaderReader
falls back on these and similar options if no relevant native elements exist. If it absolutely cannot find an alternative it
will return NULL, indicating the information could not be found in the feed. You should note that classes implementing
ZendFeedReaderFeedAbstractFeed also implement the SPL Iterator and Countable interfaces.
Feeds can also be imported from strings or files.
1
2

// from a URI
$feed = ZendFeedReaderReader::import(’http://www.planet-php.net/rdf/’);

3
4
5

// from a String
$feed = ZendFeedReaderReader::importString($feedXmlString);

6
7
8

// from a file
$feed = ZendFeedReaderReader::importFile(’./feed.xml’);

102.3 Retrieving Underlying Feed and Entry Sources
ZendFeedReaderReader does its best not to stick you in a narrow confine. If you need to work on a feed
outside of ZendFeedReaderReader, you can extract the base DOMDocument or DOMElement objects from
any class, or even an XML string containing these. Also provided are methods to extract the current DOMXPath object
(with all core and Extension namespaces registered) and the correct prefix used in all XPath queries for the current
Feed or Entry. The basic methods to use (on any object) are saveXml(), getDomDocument(), getElement(),
getXpath() and getXpathPrefix(). These will let you break free of ZendFeedReader and do whatever
else you want.
• saveXml() returns an XML string containing only the element representing the current object.
• getDomDocument() returns the DOMDocument object representing the entire feed (even if called from an
Entry object).
• getElement() returns the DOMElement of the current object (i.e. the Feed or current Entry).

418

Chapter 102. ZendFeedReaderReader
Zend Framework 2 Documentation, Release 2.2.5

• getXpath() returns the DOMXPath object for the current feed (even if called from an Entry object) with the
namespaces of the current feed type and all loaded Extensions pre-registered.
• getXpathPrefix() returns the query prefix for the current object (i.e. the Feed or current Entry) which
includes the correct XPath query path for that specific Feed or Entry.
Here’s an example where a feed might include an RSS Extension not supported by ZendFeedReaderReader
out of the box. Notably, you could write and register an Extension (covered later) to do this, but that’s not always
warranted for a quick check. You must register any new namespaces on the DOMXPath object before use unless they
are registered by ZendFeedReader or an Extension beforehand.
1
2
3
4
5
6
7

$feed
= ZendFeedReaderReader::import(’http://www.planet-php.net/rdf/’);
$xpathPrefix = $feed->getXpathPrefix();
$xpath
= $feed->getXpath();
$xpath->registerNamespace(’admin’, ’http://webns.net/mvcb/’);
$reportErrorsTo = $xpath->evaluate(’string(’
. $xpathPrefix
. ’/admin:errorReportsTo)’);

Warning: If you register an already registered namespace with a different prefix name to that used internally by
ZendFeedReaderReader, it will break the internal operation of this component.

102.4 Cache Support and Intelligent Requests
102.4.1 Adding Cache Support to ZendFeedReaderReader
ZendFeedReaderReader supports using an instance of ZendCache to cache feeds (as XML) to avoid
unnecessary network requests. Adding a cache is as simple here as it is for other Zend Framework components,
create and configure your cache and then tell ZendFeedReaderReader to use it! The cache key used is
“ZendFeedReader” followed by the MD5 hash of the feed’s URI.
1

$cache = ZendCacheStorageFactory::adapterFactory(’Memory’);

2
3

ZendFeedReaderReader::setCache($cache);

102.4.2 HTTP Conditional GET Support
The big question often asked when importing a feed frequently, is if it has even changed. With a cache enabled, you
can add HTTP Conditional GET support to your arsenal to answer that question.
Using this method, you can request feeds from URIs and include their last known ETag and Last-Modified response
header values with the request (using the If-None-Match and If-Modified-Since headers). If the feed on the server
remains unchanged, you should receive a 304 response which tells ZendFeedReaderReader to use the
cached version. If a full feed is sent in a response with a status code of 200, this means the feed has changed and
ZendFeedReaderReader will parse the new version and save it to the cache. It will also cache the new ETag
and Last-Modified header values for future use.
These “conditional” requests are not guaranteed to be supported by the server you request a URI of, but can be
attempted regardless. Most common feed sources like blogs should however have this supported. To enable conditional
requests, you will need to provide a cache to ZendFeedReaderReader.
1

$cache = ZendCacheStorageFactory::adapterFactory(’Memory’);

2
3

ZendFeedReaderReader::setCache($cache);

102.4. Cache Support and Intelligent Requests

419
Zend Framework 2 Documentation, Release 2.2.5

4

ZendFeedReaderReader::useHttpConditionalGet();

5
6

$feed = ZendFeedReaderReader::import(’http://www.planet-php.net/rdf/’);

In the example above, with HTTP Conditional GET requests enabled, the response header values for ETag and LastModified will be cached along with the feed. For the the cache’s lifetime, feeds will only be updated on the cache if a
non-304 response is received containing a valid RSS or Atom XML document.
If you intend on managing request headers from outside ZendFeedReaderReader, you can set the relevant
If-None-Matches and If-Modified-Since request headers via the URI import method.
1
2
3
4
5

$lastEtagReceived = ’5e6cefe7df5a7e95c8b1ba1a2ccaff3d’;
$lastModifiedDateReceived = ’Wed, 08 Jul 2009 13:37:22 GMT’;
$feed = ZendFeedReaderReader::import(
$uri, $lastEtagReceived, $lastModifiedDateReceived
);

102.5 Locating Feed URIs from Websites
These days, many websites are aware that the location of their XML feeds is not always obvious. A small RDF, RSS or
Atom graphic helps when the user is reading the page, but what about when a machine visits trying to identify where
your feeds are located? To assist in this, websites may point to their feeds using <link> tags in the <head> section of
their HTML. To take advantage of this, you can use ZendFeedReaderReader to locate these feeds using the
static findFeedLinks() method.
This method calls any URI and searches for the location of RSS, RDF and Atom feeds assuming the website’s HTML
contains the relevant links. It then returns a value object where you can check for the existence of a RSS, RDF or Atom
feed URI.
The returned object is an ArrayObject subclass called ZendFeedReaderFeedSet so you can cast it to an
array, or iterate over it, to access all the detected links. However, as a simple shortcut, you can just grab the first RSS,
RDF or Atom link using its public properties as in the example below. Otherwise, each element of the ArrayObject
is a simple array with the keys “type” and “uri” where the type is one of “rdf”, “rss” or “atom”.
1

$links = ZendFeedReaderReader::findFeedLinks(’http://www.planet-php.net’);

2
3
4
5
6
7
8
9
10
11

if (isset($links->rdf)) {
echo $links->rdf, "n"; // http://www.planet-php.org/rdf/
}
if (isset($links->rss)) {
echo $links->rss, "n"; // http://www.planet-php.org/rss/
}
if (isset($links->atom)) {
echo $links->atom, "n"; // http://www.planet-php.org/atom/
}

Based on these links, you can then import from whichever source you wish in the usual manner.
This quick method only gives you one link for each feed type, but websites may indicate many links of any type.
Perhaps it’s a news site with a RSS feed for each news category. You can iterate over all links using the ArrayObject’s
iterator.
1

$links = ZendFeedReader::findFeedLinks(’http://www.planet-php.net’);

2
3
4
5

foreach ($links as $link) {
echo $link[’href’], "n";
}

420

Chapter 102. ZendFeedReaderReader
Zend Framework 2 Documentation, Release 2.2.5

102.6 Attribute Collections
In an attempt to simplify return types, return types from the various feed and entry level methods may include an
object of type ZendFeedReaderCollectionAbstractCollection. Despite the special class name
which I’ll explain below, this is just a simple subclass of SPL‘s ArrayObject.
The main purpose here is to allow the presentation of as much data as possible from the requested elements, while still
allowing access to the most relevant data as a simple array. This also enforces a standard approach to returning such
data which previously may have wandered between arrays and objects.
The new class type acts identically to ArrayObject with the sole addition being a new method getValues()
which returns a simple flat array containing the most relevant information.
A simple example of this is ZendFeedReaderReaderFeedInterface::getCategories(). When
used with any RSS or Atom feed, this method will return category data as a container object called
ZendFeedReaderCollectionCategory. The container object will contain, per category, three fields
of data: term, scheme and label. The “term” is the basic category name, often machine readable (i.e. plays nice with
URIs). The scheme represents a categorisation scheme (usually a URI identifier) also known as a “domain” in RSS 2.0.
The “label” is a human readable category name which supports HTML entities. In RSS 2.0, there is no label attribute
so it is always set to the same value as the term for convenience.
To access category labels by themselves in a simple value array, you might commit to something like:
1
2
3
4
5
6

$feed = ZendFeedReaderReader::import(’http://www.example.com/atom.xml’);
$categories = $feed->getCategories();
$labels = array();
foreach ($categories as $cat) {
$labels[] = $cat[’label’]
}

It’s a contrived example, but the point is that the labels are tied up with other information.
However, the container class allows you to access the “most relevant” data as a simple array using the getValues()
method. The concept of “most relevant” is obviously a judgement call. For categories it means the category labels
(not the terms or schemes) while for authors it would be the authors’ names (not their email addresses or URIs). The
simple array is flat (just values) and passed through array_unique() to remove duplication.
1
2
3

$feed = ZendFeedReaderReader::import(’http://www.example.com/atom.xml’);
$categories = $feed->getCategories();
$labels = $categories->getValues();

The above example shows how to extract only labels and nothing else thus giving simple access to the category labels
without any additional work to extract that data by itself.

102.7 Retrieving Feed Information
Retrieving information from a feed (we’ll cover entries and items in the next section though they follow identical
principals) uses a clearly defined API which is exactly the same regardless of whether the feed in question is RSS, RDF
or Atom. The same goes for sub-versions of these standards and we’ve tested every single RSS and Atom version.
While the underlying feed XML can differ substantially in terms of the tags and elements they present, they nonetheless
are all trying to convey similar information and to reflect this all the differences and wrangling over alternative tags are
handled internally by ZendFeedReaderReader presenting you with an identical interface for each. Ideally,
you should not have to care whether a feed is RSS or Atom so long as you can extract the information you want.
Note: While determining common ground between feed types is itself complex, it should be noted that RSS in
particular is a constantly disputed “specification”. This has its roots in the original RSS 2.0 document which contains
102.6. Attribute Collections

421
Zend Framework 2 Documentation, Release 2.2.5

ambiguities and does not detail the correct treatment of all elements. As a result, this component rigorously applies the
RSS 2.0.11 Specification published by the RSS Advisory Board and its accompanying RSS Best Practices Profile. No
other interpretation of RSS 2.0 will be supported though exceptions may be allowed where it does not directly prevent
the application of the two documents mentioned above.
Of course, we don’t live in an ideal world so there may be times the API just does not cover what you’re looking
for. To assist you, ZendFeedReaderReader offers a plugin system which allows you to write Extensions to
expand the core API and cover any additional data you are trying to extract from feeds. If writing another Extension
is too much trouble, you can simply grab the underlying DOM or XPath objects and do it by hand in your application.
Of course, we really do encourage writing an Extension simply to make it more portable and reusable, and useful
Extensions may be proposed to the Framework for formal addition.
Here’s a summary of the Core API for Feeds. You should note it comprises not only the basic RSS and Atom standards,
but also accounts for a number of included Extensions bundled with ZendFeedReaderReader. The naming
of these Extension sourced methods remain fairly generic - all Extension methods operate at the same level as the Core
API though we do allow you to retrieve any specific Extension object separately if required.

422

Chapter 102. ZendFeedReaderReader
Zend Framework 2 Documentation, Release 2.2.5

Table 102.1: Feed Level API Methods
getId()
getTitle()
getDescription()
getLink()

getFeedLink()

getAuthors()

getAuthor(integer
$index = 0)
getDateCreated()
getDateModified()
getLastBuildDate()
getLanguage()
getGenerator()
getCopyright()
getHubs()
getCategories()

getImage()

Returns a unique ID associated with this feed
Returns the title of the feed
Returns the text description of the feed.
Returns a URI to the HTML website containing the same or similar information as this feed
(i.e. if the feed is from a blog, it should provide the blog’s URI where the HTML version of
the entries can be read).
Returns the URI of this feed, which may be the same as the URI used to import the feed.
There are important cases where the feed link may differ because the source URI is being
updated and is intended to be removed in the future.
Returns an object of type ZendFeedReaderCollectionAuthor which is an ArrayObject whose
elements are each simple arrays containing any combination of the keys “name”, “email” and
“uri”. Where irrelevant to the source data, some of these keys may be omitted.
Returns either the first author known, or with the optional $index parameter any specific index
on the array of Authors as described above (returning NULL if an invalid index).
Returns the date on which this feed was created. Generally only applicable to Atom where it
represents the date the resource described by an Atom 1.0 document was created. The
returned date will be a DateTime object.
Returns the date on which this feed was last modified. The returned date will be a DateTime
object.
Returns the date on which this feed was last built. The returned date will be a DateTime
object. This is only supported by RSS - Atom feeds will always return NULL.
Returns the language of the feed (if defined) or simply the language noted in the XML
document.
Returns the generator of the feed, e.g. the software which generated it. This may differ
between RSS and Atom since Atom defines a different notation.
Returns any copyright notice associated with the feed.
Returns an array of all Hub Server URI endpoints which are advertised by the feed for use
with the Pubsubhubbub Protocol, allowing subscriptions to the feed for real-time updates.
Returns a ZendFeedReaderCollectionCategory object containing the details of any categories
associated with the overall feed. The supported fields include “term” (the machine readable
category name), “scheme” (the categorisation scheme and domain for this category), and
“label” (a HTML decoded human readable category name). Where any of the three fields are
absent from the field, they are either set to the closest available alternative or, in the case of
“scheme”, set to NULL.
Returns an array containing data relating to any feed image or logo, or NULL if no image
found. The resulting array may contain the following keys: uri, link, title, description, height,
and width. Atom logos only contain a URI so the remaining metadata is drawn from RSS
feeds only.

Given the variety of feeds in the wild, some of these methods will undoubtedly return NULL indicating the relevant
information couldn’t be located. Where possible, ZendFeedReaderReader will fall back on alternative elements during its search. For example, searching an RSS feed for a modification date is more complicated than it looks.
RSS 2.0 feeds should include a <lastBuildDate> tag and (or) a <pubDate> element. But what if it doesn’t,
maybe this is an RSS 1.0 feed? Perhaps it instead has an <atom:updated> element with identical information
(Atom may be used to supplement RSS‘s syntax)? Failing that, we could simply look at the entries, pick the most recent, and use its <pubDate> element. Assuming it exists... Many feeds also use Dublin Core 1.0 or 1.1 <dc:date>
elements for feeds and entries. Or we could find Atom lurking again.
The point is, ZendFeedReaderReader was designed to know this. When you ask for the modification date
(or anything else), it will run off and search for all these alternatives until it either gives up and returns NULL, or finds
an alternative that should have the right answer.

102.7. Retrieving Feed Information

423
Zend Framework 2 Documentation, Release 2.2.5

In addition to the above methods, all Feed objects implement methods for retrieving the DOM and XPath objects for
the current feeds as described earlier. Feed objects also implement the SPL Iterator and Countable interfaces. The
extended API is summarised below.
Table 102.2: Extended Feed Level API Methods
getDomDocument()
getElement()
saveXml()
getXpath()
getXpathPrefix()
getEncoding()

count()
current()
key()
next()
rewind()
valid()
getExtensions()
getExtension(string
$name)
getType()

Returns the parent DOMDocument object for the entire source XML document
Returns the current feed level DOMElement object
Returns a string containing an XML document of the entire feed element (this is not the
original document but a rebuilt version)
Returns the DOMXPath object used internally to run queries on the DOMDocument object
(this includes core and Extension namespaces pre-registered)
Returns the valid DOM path prefix prepended to all XPath queries matching the feed being
queried
Returns the encoding of the source XML document (note: this cannot account for errors such
as the server sending documents in a different encoding). Where not defined, the default UTF-8
encoding of Unicode is applied.
Returns a count of the entries or items this feed contains (implements SPLCountable interface)
Returns either the current entry (using the current index from key())
Returns the current entry index
Increments the entry index value by one
Resets the entry index to 0
Checks that the current entry index is valid, i.e. it does fall below 0 and does not exceed the
number of entries existing.
Returns an array of all Extension objects loaded for the current feed (note: both feed-level and
entry-level Extensions exist, and only feed-level Extensions are returned here). The array keys
are of the form {ExtensionName}_Feed.
Returns an Extension object for the feed registered under the provided name. This allows more
fine-grained access to Extensions which may otherwise be hidden within the implementation of
the standard API methods.
Returns a static class constant (e.g. ZendFeedReaderReader::TYPE_ATOM_03, i.e. Atom 0.3)
indicating exactly what kind of feed is being consumed.

102.8 Retrieving Entry/Item Information
Retrieving information for specific entries or items (depending on whether you speak Atom or RSS) is identical to feed
level data. Accessing entries is simply a matter of iterating over a Feed object or using the SPL Iterator interface
Feed objects implement and calling the appropriate method on each.

424

Chapter 102. ZendFeedReaderReader
Zend Framework 2 Documentation, Release 2.2.5

Table 102.3: Entry Level API Methods
getId()
getTitle()
getDescription()
getLink()
getPermaLink()
getAuthors()

getAuthor(integer $index
= 0)
getDateCreated()

getDateModified()
getContent()

getEnclosure()

getCommentCount()
getCommentLink()
getCommentFeedLink([string $type =
‘atom’|’rss’])
getCategories()

Returns a unique ID for the current entry.
Returns the title of the current entry.
Returns a description of the current entry.
Returns a URI to the HTML version of the current entry.
Returns the permanent link to the current entry. In most cases, this is the same as
using getLink().
Returns an object of type ZendFeedReaderCollectionAuthor which is an
ArrayObject whose elements are each simple arrays containing any combination of
the keys “name”, “email” and uri”. Where irrelevant to the source data, some of
these keys may be omitted.
Returns either the first author known, or with the optional $index parameter any
specific index on the array of Authors as described above (returning NULL if an
invalid index).
Returns the date on which the current entry was created. Generally only applicable
to Atom where it represents the date the resource described by an Atom 1.0
document was created.
Returns the date on which the current entry was last modified
Returns the content of the current entry (this has any entities reversed if possible
assuming the content type is HTML). The description is returned if a separate
content element does not exist.
Returns an array containing the value of all attributes from a multi-media
<enclosure> element including as array keys: url, length, type. In accordance with
the RSS Best Practices Profile of the RSS Advisory Board, no support is offers for
multiple enclosures since such support forms no part of the RSS specification.
Returns the number of comments made on this entry at the time the feed was last
generated
Returns a URI pointing to the HTML page where comments can be made on this
entry
Returns a URI pointing to a feed of the provided type containing all comments for
this entry (type defaults to Atom/RSS depending on current feed type).
Returns a ZendFeedReaderCollectionCategory object containing the details of any
categories associated with the entry. The supported fields include “term” (the
machine readable category name), “scheme” (the categorisation scheme and domain
for this category), and “label” (a HTML decoded human readable category name).
Where any of the three fields are absent from the field, they are either set to the
closest available alternative or, in the case of “scheme”, set to NULL.

The extended API for entries is identical to that for feeds with the exception of the Iterator methods which are not
needed here.

102.8. Retrieving Entry/Item Information

425
Zend Framework 2 Documentation, Release 2.2.5

Caution: There is often confusion over the concepts of modified and created dates. In Atom, these are two clearly
defined concepts (so knock yourself out) but in RSS they are vague. RSS 2.0 defines a single <pubDate> element
which typically refers to the date this entry was published, i.e. a creation date of sorts. This is not always the case,
and it may change with updates or not. As a result, if you really want to check whether an entry has changed, don’t
rely on the results of getDateModified(). Instead, consider tracking the MD5 hash of three other elements
concatenated, e.g. using getTitle(), getDescription() and getContent(). If the entry was truly
updated, this hash computation will give a different result than previously saved hashes for the same entry. This is
obviously content oriented, and will not assist in detecting changes to other relevant elements. Atom feeds should
not require such steps.
Further muddying the waters, dates in feeds may follow different standards. Atom and Dublin Core dates should
follow ISO 8601, and RSS dates should follow RFC 822 or RFC 2822 which is also common. Date methods
will throw an exception if DateTime cannot load the date string using one of the above standards, or the PHP
recognised possibilities for RSS dates.
Warning: The values returned from these methods are not validated. This means users must perform validation
on all retrieved data including the filtering of any HTML such as from getContent() before it is output from
your application. Remember that most feeds come from external sources, and therefore the default assumption
should be that they cannot be trusted.

Table 102.4: Extended Entry Level API Methods
getDomDocument()
getElement()
getXpath()
getXpathPrefix()
getEncoding()

getExtensions()
getExtension(string
$name)
getType()

Returns the parent DOMDocument object for the entire feed (not just the current entry)
Returns the current entry level DOMElement object
Returns the DOMXPath object used internally to run queries on the DOMDocument object
(this includes core and Extension namespaces pre-registered)
Returns the valid DOM path prefix prepended to all XPath queries matching the entry being
queried
Returns the encoding of the source XML document (note: this cannot account for errors such
as the server sending documents in a different encoding). The default encoding applied in the
absence of any other is the UTF-8 encoding of Unicode.
Returns an array of all Extension objects loaded for the current entry (note: both feed-level and
entry-level Extensions exist, and only entry-level Extensions are returned here). The array keys
are in the form {ExtensionName}Entry.
Returns an Extension object for the entry registered under the provided name. This allows more
fine-grained access to Extensions which may otherwise be hidden within the implementation of
the standard API methods.
Returns a static class constant (e.g. ZendFeedReaderReader::TYPE_ATOM_03, i.e. Atom 0.3)
indicating exactly what kind of feed is being consumed.

102.9 Extending Feed and Entry APIs
Extending ZendFeedReaderReader allows you to add methods at both the feed and entry level which cover
the retrieval of information not already supported by ZendFeedReaderReader. Given the number of RSS
and Atom extensions that exist, this is a good thing since ZendFeedReaderReader couldn’t possibly add
everything.
There are two types of Extensions possible, those which retrieve information from elements which are
immediate children of the root element (e.g.
<channel> for RSS or <feed> for Atom) and those
who retrieve information from child elements of an entry (e.g.
<item> for RSS or <entry> for
Atom). On the filesystem these are grouped as classes within a namespace based on the extension stan-

426

Chapter 102. ZendFeedReaderReader
Zend Framework 2 Documentation, Release 2.2.5

dard’s name. For example, internally we have ZendFeedReaderExtensionDublinCoreFeed and
ZendFeedReaderExtensionDublinCoreEntry classes which are two Extensions implementing
Dublin Core 1.0 and 1.1 support.
Extensions are loaded into ZendFeedReaderReader using a ZendServiceManagerAbstractPluginManager
implementation, ZendFeedReaderExtensionManager, so its operation will be familiar from other Zend
Framework components. ZendFeedReaderReader already bundles a number of these Extensions, however
those which are not used internally and registered by default (so called Core Extensions) must be registered to
ZendFeedReaderReader before they are used. The bundled Extensions include:
Table 102.5: Core Extensions (pre-registered)
DublinCore (Feed and Entry)
Content (Entry only)
Atom (Feed and Entry)
Slash
WellFormedWeb
Thread
Podcast

Implements support for Dublin Core Metadata Element Set 1.0 and 1.1
Implements support for Content 1.0
Implements support for Atom 0.3 and Atom 1.0
Implements support for the Slash RSS 1.0 module
Implements support for the Well Formed Web CommentAPI 1.0
Implements support for Atom Threading Extensions as described in RFC 4685
Implements support for the Podcast 1.0 DTD from Apple

The Core Extensions are somewhat special since they are extremely common and multi-faceted. For example, we have
a Core Extension for Atom. Atom is implemented as an Extension (not just a base class) because it doubles as a valid
RSS module - you can insert Atom elements into RSS feeds. I’ve even seen RDF feeds which use a lot of Atom in
place of more common Extensions like Dublin Core.
Table 102.6: Non-Core Extensions (must register manually)
Syndication
CreativeCommons

Implements Syndication 1.0 support for RSS feeds
A RSS module that adds an element at the <channel> or <item> level that specifies which
Creative Commons license applies.

The additional non-Core Extensions are offered but not registered to ZendFeedReaderReader by default. If
you want to use them, you’ll need to tell ZendFeedReaderReader to load them in advance of importing a
feed. Additional non-Core Extensions will be included in future iterations of the component.
Registering an Extension with ZendFeedReaderReader, so it is loaded and its API is available to Feed and
Entry objects, is a simple affair using the ZendFeedReaderExtensionManager. Here we register the
optional Syndication Extension, and discover that it can be directly called from the Entry level API without any effort.
Note that Extension names are case sensitive and use camel casing for multiple terms.
1
2
3

ZendFeedReaderReader::registerExtension(’Syndication’);
$feed = ZendFeedReaderReader::import(’http://rss.slashdot.org/Slashdot/slashdot’);
$updatePeriod = $feed->getUpdatePeriod();

In the simple example above, we checked how frequently a feed is being updated using the getUpdatePeriod()
method. Since it’s not part of ZendFeedReaderReader‘s core API, it could only be a method supported by
the newly registered Syndication Extension.
As you can also notice, the new methods from Extensions are accessible from the main API using PHP‘s magic
methods. As an alternative, you can also directly access any Extension object for a similar result as seen below.
1
2
3
4

ZendFeedReaderReader::registerExtension(’Syndication’);
$feed = ZendFeedReaderReader::import(’http://rss.slashdot.org/Slashdot/slashdot’);
$syndication = $feed->getExtension(’Syndication’);
$updatePeriod = $syndication->getUpdatePeriod();

102.9. Extending Feed and Entry APIs

427
Zend Framework 2 Documentation, Release 2.2.5

102.9.1 Writing ZendFeedReaderReader Extensions
Inevitably, there will be times when the ZendFeedReaderReader API is just not capable of getting something
you need from a feed or entry. You can use the underlying source objects, like DOMDocument, to get these by hand
however there is a more reusable method available by writing Extensions supporting these new queries.
As an example, let’s take the case of a purely fictitious corporation named Jungle Books. Jungle Books have been
publishing a lot of reviews on books they sell (from external sources and customers), which are distributed as an RSS
2.0 feed. Their marketing department realises that web applications using this feed cannot currently figure out exactly
what book is being reviewed. To make life easier for everyone, they determine that the geek department needs to
extend RSS 2.0 to include a new element per entry supplying the ISBN-10 or ISBN-13 number of the publication the
entry concerns. They define the new <isbn> element quite simply with a standard name and namespace URI:
1
2

JungleBooks 1.0:
http://example.com/junglebooks/rss/module/1.0/

A snippet of RSS containing this extension in practice could be something similar to:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

<?xml version="1.0" encoding="utf-8" ?>
<rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:jungle="http://example.com/junglebooks/rss/module/1.0/">
<channel>
<title>Jungle Books Customer Reviews</title>
<link>http://example.com/junglebooks</link>
<description>Many book reviews!</description>
<pubDate>Fri, 26 Jun 2009 19:15:10 GMT</pubDate>
<jungle:dayPopular>
http://example.com/junglebooks/book/938
</jungle:dayPopular>
<item>
<title>Review Of Flatland: A Romance of Many Dimensions</title>
<link>http://example.com/junglebooks/review/987</link>
<author>Confused Physics Student</author>
<content:encoded>
A romantic square?!
</content:encoded>
<pubDate>Thu, 25 Jun 2009 20:03:28 -0700</pubDate>
<jungle:isbn>048627263X</jungle:isbn>
</item>
</channel>
</rss>

Implementing this new ISBN element as a simple entry level extension would require the following class (using your
own class namespace outside of Zend).
1
2
3
4
5
6
7
8
9
10
11
12
13

class MyFeedReaderExtensionJungleBooksEntry
extends ZendFeedReaderExtensionAbstractEntry
{
public function getIsbn()
{
if (isset($this->data[’isbn’])) {
return $this->data[’isbn’];
}
$isbn = $this->xpath->evaluate(
’string(’ . $this->getXpathPrefix() . ’/jungle:isbn)’
);
if (!$isbn) {
$isbn = null;

428

Chapter 102. ZendFeedReaderReader
Zend Framework 2 Documentation, Release 2.2.5

}
$this->data[’isbn’] = $isbn;
return $this->data[’isbn’];

14
15
16

}

17
18

protected function registerNamespaces()
{
$this->xpath->registerNamespace(
’jungle’, ’http://example.com/junglebooks/rss/module/1.0/’
);
}

19
20
21
22
23
24
25

}

This extension is easy enough to follow. It creates a new method getIsbn() which runs an XPath query on the
current entry to extract the ISBN number enclosed by the <jungle:isbn> element. It can optionally store this to
the internal non-persistent cache (no need to keep querying the DOM if it’s called again on the same entry). The value
is returned to the caller. At the end we have a protected method (it’s abstract so it must exist) which registers the Jungle
Books namespace for their custom RSS module. While we call this an RSS module, there’s nothing to prevent the same
element being used in Atom feeds - and all Extensions which use the prefix provided by getXpathPrefix() are
actually neutral and work on RSS or Atom feeds with no extra code.
Since this Extension is stored outside of Zend Framework, you’ll need to register the path prefix for your Extensions
so ZendLoaderPluginLoader can find them. After that, it’s merely a matter of registering the Extension, if
it’s not already loaded, and using it in practice.
1
2
3
4
5
6

if (!ZendFeedReaderReader::isRegistered(’JungleBooks’)) {
$extensions = ZendFeedReaderReader::getExtensionManager();
$extensions->setInvokableClass(’JungleBooksEntry’, ’MyFeedReaderExtensionJungleBooksEntry’);
ZendFeedReaderReader::registerExtension(’JungleBooks’);
}
$feed = ZendFeedReaderReader::import(’http://example.com/junglebooks/rss’);

7
8
9

// ISBN for whatever book the first entry in the feed was concerned with
$firstIsbn = $feed->current()->getIsbn();

Writing a feed level Extension is not much different. The example feed from earlier included an unmentioned
<jungle:dayPopular> element which Jungle Books have added to their standard to include a link to the day’s
most popular book (in terms of visitor traffic). Here’s an Extension which adds a getDaysPopularBookLink()
method to the feel level API.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

class MyFeedReaderExtensionJungleBooksFeed
extends ZendFeedReaderExtensionAbstractFeed
{
public function getDaysPopularBookLink()
{
if (isset($this->data[’dayPopular’])) {
return $this->data[’dayPopular’];
}
$dayPopular = $this->xpath->evaluate(
’string(’ . $this->getXpathPrefix() . ’/jungle:dayPopular)’
);
if (!$dayPopular) {
$dayPopular = null;
}
$this->data[’dayPopular’] = $dayPopular;
return $this->data[’dayPopular’];
}

18

102.9. Extending Feed and Entry APIs

429
Zend Framework 2 Documentation, Release 2.2.5

protected function registerNamespaces()
{
$this->xpath->registerNamespace(
’jungle’, ’http://example.com/junglebooks/rss/module/1.0/’
);
}

19
20
21
22
23
24
25

}

Let’s repeat the last example using a custom Extension to show the method being used.
1
2
3
4
5
6

if (!ZendFeedReaderReader::isRegistered(’JungleBooks’)) {
$extensions = ZendFeedReaderReader::getExtensionManager();
$extensions->setInvokableClass(’JungleBooksFeed’, ’MyFeedReaderExtensionJungleBooksFeed’);
ZendFeedReaderReader::registerExtension(’JungleBooks’);
}
$feed = ZendFeedReaderReader::import(’http://example.com/junglebooks/rss’);

7
8
9

// URI to the information page of the day’s most popular book with visitors
$daysPopularBookLink = $feed->getDaysPopularBookLink();

Going through these examples, you’ll note that we don’t register feed and entry Extensions separately. Extensions
within the same standard may or may not include both a feed and entry class, so ZendFeedReaderReader
only requires you to register the overall parent name, e.g. JungleBooks, DublinCore, Slash. Internally, it can check
at what level Extensions exist and load them up if found. In our case, we have a full set of Extensions now:
JungleBooksFeed and JungleBooksEntry.

430

Chapter 102. ZendFeedReaderReader
CHAPTER 103

ZendFeedWriterWriter

103.1 Introduction
ZendFeedWriterWriter is the sibling component to ZendFeedReaderReader responsible for generating feeds for output. It supports the Atom 1.0 specification (RFC 4287) and RSS 2.0 as specified by the RSS
Advisory Board (RSS 2.0.11). It does not deviate from these standards. It does, however, offer a simple Extension
system which allows for any extension and module for either of these two specifications to be implemented if they are
not provided out of the box.
In many ways, ZendFeedWriterWriter is the inverse of ZendFeedReaderReader. Where
ZendFeedReaderReader focuses on providing an easy to use architecture fronted by getter methods,
ZendFeedWriterWriter is fronted by similarly named setters or mutators. This ensures the API won’t
pose a learning curve to anyone familiar with ZendFeedReaderReader.
As a result of this design, the rest may even be obvious.
Behind the scenes, data set on any
ZendFeedWriterWriter Data Container object is translated at render time onto a DOMDocument object
using the necessary feed elements. For each supported feed type there is both an Atom 1.0 and RSS 2.0 renderer.
Using a DOMDocument class rather than a templating solution has numerous advantages, the most obvious being
the ability to export the DOMDocument for additional processing and relying on PHP DOM for correct and valid
rendering.

103.2 Architecture
The architecture of ZendFeedWriterWriter is very simple. It has two core sets of classes: data containers
and renderers.
The containers include the ZendFeedWriterFeed and ZendFeedWriterEntry classes. The Entry
classes can be attached to any Feed class. The sole purpose of these containers is to collect data about the feed to
generate using a simple interface of setter methods. These methods perform some data validity testing. For example,
it will validate any passed URIs, dates, etc. These checks are not tied to any of the feed standards definitions. The
container objects also contain methods to allow for fast rendering and export of the final feed, and these can be reused
at will.
In addition to the main data container classes, there are two additional Atom 2.0 specific classes.
ZendFeedWriterSource and ZendFeedWriterDeleted. The former implements Atom 2.0 source
elements which carry source feed metadata for a specific entry within an aggregate feed (i.e. the current feed is not

431
Zend Framework 2 Documentation, Release 2.2.5

the entry’s original source). The latter implements the Atom Tombstones RFC allowing feeds to carry references to
entries which have been deleted.
While there are two main data container types, there are four renderers - two matching container renderers per supported feed type. Each renderer accepts a container, and based on its content attempts to generate valid feed markup.
If the renderer is unable to generate valid feed markup, perhaps due to the container missing an obligatory data point,
it will report this by throwing an Exception. While it is possible to ignore Exceptions, this removes the default
safeguard of ensuring you have sufficient data set to render a wholly valid feed.
To explain this more clearly, you may construct a set of data containers for a feed where there is a Feed container, into
which has been added some Entry containers and a Deleted container. This forms a data hierarchy resembling a normal
feed. When rendering is performed, this hierarchy has its pieces passed to relevant renderers and the partial feeds (all
DOMDocuments) are then pieced together to create a complete feed. In the case of Source or Deleted (Tomestone)
containers, these are rendered only for Atom 2.0 and ignored for RSS.
Due to the system being divided between data containers and renderers, it can make Extensions somewhat interesting.
A typical Extension offering namespaced feed and entry level elements, must itself reflect the exact same architecture,
i.e. offer feed and entry level data containers, and matching renderers. There is, fortunately, no complex integration
work required since all Extension classes are simply registered and automatically used by the core classes. We’ll meet
Extensions in more detail at the end of this section.

103.3 Getting Started
Using ZendFeedWriterWriter is as simple as setting data and triggering the renderer. Here is an example
to generate a minimal Atom 1.0 feed. As this demonstrates, each feed or entry uses a separate data container.
1
2
3
4
5
6
7
8
9
10
11
12
13
14

/**
* Create the parent feed
*/
$feed = new ZendFeedWriterFeed;
$feed->setTitle(’Paddy’s Blog’);
$feed->setLink(’http://www.example.com’);
$feed->setFeedLink(’http://www.example.com/atom’, ’atom’);
$feed->addAuthor(array(
’name’ => ’Paddy’,
’email’ => ’paddy@example.com’,
’uri’
=> ’http://www.example.com’,
));
$feed->setDateModified(time());
$feed->addHub(’http://pubsubhubbub.appspot.com/’);

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

/**
* Add one or more entries. Note that entries must
* be manually added once created.
*/
$entry = $feed->createEntry();
$entry->setTitle(’All Your Base Are Belong To Us’);
$entry->setLink(’http://www.example.com/all-your-base-are-belong-to-us’);
$entry->addAuthor(array(
’name’ => ’Paddy’,
’email’ => ’paddy@example.com’,
’uri’
=> ’http://www.example.com’,
));
$entry->setDateModified(time());
$entry->setDateCreated(time());
$entry->setDescription(’Exposing the difficultly of porting games to English.’);

432

Chapter 103. ZendFeedWriterWriter
Zend Framework 2 Documentation, Release 2.2.5

31
32
33
34

$entry->setContent(
’I am not writing the article. The example is long enough as is ;).’
);
$feed->addEntry($entry);

35
36
37
38
39
40

/**
* Render the resulting feed to Atom 1.0 and assign to $out.
* You can substitute "atom" with "rss" to generate an RSS 2.0 feed.
*/
$out = $feed->export(’atom’);

The output rendered should be as follows:
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

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title type="text">Paddy’s Blog</title>
<subtitle type="text">Writing about PC Games since 176 BC.</subtitle>
<updated>2009-12-14T20:28:18+00:00</updated>
<generator uri="http://framework.zend.com" version="1.10.0alpha">
ZendFeedWriter
</generator>
<link rel="alternate" type="text/html" href="http://www.example.com"/>
<link rel="self" type="application/atom+xml"
href="http://www.example.com/atom"/>
<id>http://www.example.com</id>
<author>
<name>Paddy</name>
<email>paddy@example.com</email>
<uri>http://www.example.com</uri>
</author>
<link rel="hub" href="http://pubsubhubbub.appspot.com/"/>
<entry>
<title type="html"><![CDATA[All Your Base Are Belong To
Us]]></title>
<summary type="html">
<![CDATA[Exposing the difficultly of porting games to
English.]]>
</summary>
<published>2009-12-14T20:28:18+00:00</published>
<updated>2009-12-14T20:28:18+00:00</updated>
<link rel="alternate" type="text/html"
href="http://www.example.com/all-your-base-are-belong-to-us"/>
<id>http://www.example.com/all-your-base-are-belong-to-us</id>
<author>
<name>Paddy</name>
<email>paddy@example.com</email>
<uri>http://www.example.com</uri>
</author>
<content type="html">
<![CDATA[I am not writing the article.
The example is long enough as is ;).]]>
</content>
</entry>
</feed>

This is a perfectly valid Atom 1.0 example. It should be noted that omitting an obligatory point of data, such as a title,
will trigger an Exception when rendering as Atom 1.0. This will differ for RSS 2.0 since a title may be omitted so
long as a description is present. This gives rise to Exceptions that differ between the two standards depending on the

103.3. Getting Started

433
Zend Framework 2 Documentation, Release 2.2.5

renderer in use. By design, ZendFeedWriterWriter will not render an invalid feed for either standard unless
the end-user deliberately elects to ignore all Exceptions. This built in safeguard was added to ensure users without
in-depth knowledge of the relevant specifications have a bit less to worry about.

103.4 Setting Feed Data Points
Before you can render a feed, you must first setup the data necessary for the feed being rendered. This utilises a
simple setter style API which doubles as an initial method for validating the data being set. By design, the API closely
matches that for ZendFeedReaderReader to avoid undue confusion and uncertainty.
Note: Users have commented that the lack of a simple array based notation for input data gives rise to lengthy tracts
of code. This will be addressed in a future release.
ZendFeedWriterWriter offers this API via its data container classes ZendFeedWriterFeed and
ZendFeedWriterEntry (not to mention the Atom 2.0 specific and Extension classes). These classes merely
store all feed data in a type-agnostic manner, meaning you may reuse any data container with any renderer without
requiring additional work. Both classes are also amenable to Extensions, meaning that an Extension may define its
own container classes which are registered to the base container classes as extensions, and are checked when any
method call triggers the base container’s __call() method.
Here’s a summary of the Core API for Feeds. You should note it comprises not only the basic RSS and Atom standards,
but also accounts for a number of included Extensions bundled with ZendFeedWriterWriter. The naming
of these Extension sourced methods remain fairly generic - all Extension methods operate at the same level as the Core
API though we do allow you to retrieve any specific Extension object separately if required.
The Feed Level API for data is contained in ZendFeedWriterFeed. In addition to the API detailed below,
the class also implements the Countable and Iterator interfaces.

434

Chapter 103. ZendFeedWriterWriter
Zend Framework 2 Documentation, Release 2.2.5

Table 103.1: Feed Level API Methods
setId()

setTitle()
setDescription()
setLink()

Set a unique ID associated with this feed. For Atom 1.0 this is an atom:id element, whereas for RSS
2.0 it is added as a guid element. These are optional so long as a link is added, i.e. the link is set as
the ID.
Set the title of the feed.
Set the text description of the feed.

Set a URI to the HTML website containing the same or similar information as this feed (i.e. if the
feed is from a blog, it should provide the blog’s URI where the HTML version of the entries can be
read).
setAdd a link to an XML feed, whether the feed being generated or an alternate URI pointing to the
FeedLinks() same feed but in a different format. At a minimum, it is recommended to include a link to the feed
being generated so it has an identifiable final URI allowing a client to track its location changes
without necessitating constant redirects. The parameter is an array of arrays, where each sub-array
contains the keys “type” and “uri”. The type should be one of “atom”, “rss”, or “rdf”.
addAuSets the data for authors. The parameter is an array of arrays where each sub-array may contain the
thors()
keys “name”, “email” and “uri”. The “uri” value is only applicable for Atom feeds since RSS
contains no facility to show it. For RSS 2.0, rendering will create two elements - an author element
containing the email reference with the name in brackets, and a Dublin Core creator element only
containing the name.
addAuSets the data for a single author following the same array format as described above for a single
thor()
sub-array.
setDateSets the date on which this feed was created. Generally only applicable to Atom where it represents
Created()
the date the resource described by an Atom 1.0 document was created. The expected parameter may
be a UNIX timestamp or a DateTime object.
setDateSets the date on which this feed was last modified. The expected parameter may be a UNIX
Modified() timestamp or a DateTime object.
setLastSets the date on which this feed was last build. The expected parameter may be a UNIX timestamp
Buildor a DateTime object. This will only be rendered for RSS 2.0 feeds and is automatically rendered as
Date()
the current date by default when not explicitly set.
setSets the language of the feed. This will be omitted unless set.
Language()
setGenera- Allows the setting of a generator. The parameter should be an array containing the keys “name”,
tor()
“version” and “uri”. If omitted a default generator will be added referencing ZendFeedWriter, the
current Zend Framework version and the Framework’s URI.
setCopySets a copyright notice associated with the feed.
right()
addHubs()
Accepts an array of Pubsubhubbub Hub Endpoints to be rendered in the feed as Atom links so that
PuSH Subscribers may subscribe to your feed. Note that you must implement a Pubsubhubbub
Publisher in order for real-time updates to be enabled. A Publisher may be implemented using
ZendFeedPubsubhubbubPublisher. The method addHub() allows adding a single hub at a time.
addCateAccepts an array of categories for rendering, where each element is itself an array whose possible
gories()
keys include “term”, “label” and “scheme”. The “term” is a typically a category name suitable for
inclusion in a URI. The “label” may be a human readable category name supporting special
characters (it is HTML encoded during rendering) and is a required key. The “scheme” (called the
domain in RSS) is optional but must be a valid URI. The method addCategory() allows adding a
single category at a time.
setImage() Accepts an array of image metadata for an RSS image or Atom logo. Atom 1.0 only requires a URI.
RSS 2.0 requires a URI, HTML link, and an image title. RSS 2.0 optionally may send a width,
height and image description. The array parameter may contain these using the keys: uri, link, title,
description, height and width. The RSS 2.0 HTML link should point to the feed source’s HTML
page.
createEnReturns a new instance of ZendFeedWriterEntry. This is the Entry level data container. New entries
try()
are not automatically assigned to the current feed, so you must explicitly call addEntry() to add the
entry for rendering.
103.4. Setting Feed Data Points
435
addEntry() Adds an instance of ZendFeedWriterEntry to the current feed container for rendering.
createReturns a new instance of ZendFeedWriterDeleted. This is the Atom 2.0 Tombstone level data
Tombcontainer. New entries are not automatically assigned to the current feed, so you must explicitly call
Zend Framework 2 Documentation, Release 2.2.5

Note: In addition to these setters, there are also matching getters to retrieve data from the Entry data container. For
example, setImage() is matched with a getImage() method.

103.5 Setting Entry Data Points
Here’s a summary of the Core API for Entries and Items. You should note it comprises not only the basic RSS and Atom
standards, but also accounts for a number of included Extensions bundled with ZendFeedWriterWriter. The
naming of these Extension sourced methods remain fairly generic - all Extension methods operate at the same level as
the Core API though we do allow you to retrieve any specific Extension object separately if required.
The Entry Level API for data is contained in ZendFeedWriterEntry.

436

Chapter 103. ZendFeedWriterWriter
Zend Framework 2 Documentation, Release 2.2.5

Table 103.2: Entry Level API Methods
setId()

setTitle()
setDescription()
setContent()
setLink()

setFeedLinks()

addAuthors()

addAuthor()
setDateCreated()

setDateModified()
setCopyright()
setCategories()

setCommentCount()
setCommentLink()
setCommentFeedLink()
setCommentFeedLinks()
setEncoding()

Set a unique ID associated with this entry. For Atom 1.0 this is an atom:id element, whereas for
RSS 2.0 it is added as a guid element. These are optional so long as a link is added, i.e. the link
is set as the ID.
Set the title of the entry.
Set the text description of the entry.
Set the content of the entry.
Set a URI to the HTML website containing the same or similar information as this entry (i.e. if
the feed is from a blog, it should provide the blog article’s URI where the HTML version of the
entry can be read).
Add a link to an XML feed, whether the feed being generated or an alternate URI pointing to the
same feed but in a different format. At a minimum, it is recommended to include a link to the
feed being generated so it has an identifiable final URI allowing a client to track its location
changes without necessitating constant redirects. The parameter is an array of arrays, where each
sub-array contains the keys “type” and “uri”. The type should be one of “atom”, “rss”, or “rdf”.
If a type is omitted, it defaults to the type used when rendering the feed.
Sets the data for authors. The parameter is an array of arrays where each sub-array may contain
the keys “name”, “email” and “uri”. The “uri” value is only applicable for Atom feeds since RSS
contains no facility to show it. For RSS 2.0, rendering will create two elements - an author
element containing the email reference with the name in brackets, and a Dublin Core creator
element only containing the name.
Sets the data for a single author following the same format as described above for a single
sub-array.
Sets the date on which this feed was created. Generally only applicable to Atom where it
represents the date the resource described by an Atom 1.0 document was created. The expected
parameter may be a UNIX timestamp or a DateTime object. If omitted, the date used will be the
current date and time.
Sets the date on which this feed was last modified. The expected parameter may be a UNIX
timestamp or a DateTime object. If omitted, the date used will be the current date and time.
Sets a copyright notice associated with the feed.
Accepts an array of categories for rendering, where each element is itself an array whose
possible keys include “term”, “label” and “scheme”. The “term” is a typically a category name
suitable for inclusion in a URI. The “label” may be a human readable category name supporting
special characters (it is encoded during rendering) and is a required key. The “scheme” (called
the domain in RSS) is optional but must be a valid URI.
Sets the number of comments associated with this entry. Rendering differs between RSS and
Atom 2.0 depending on the element or attribute needed.
Seta a link to a HTML page containing comments associated with this entry.
Sets a link to a XML feed containing comments associated with this entry. The parameter is an
array containing the keys “uri” and “type”, where the type is one of “rdf”, “rss” or “atom”.
Same as setCommentFeedLink() except it accepts an array of arrays, where each subarray
contains the expected parameters of setCommentFeedLink().
Sets the encoding of entry text. This will default to UTF-8 which is the preferred encoding.

Note: In addition to these setters, there are also matching getters to retrieve data from the Entry data container.

103.5. Setting Entry Data Points

437
Zend Framework 2 Documentation, Release 2.2.5

438

Chapter 103. ZendFeedWriterWriter
CHAPTER 104

ZendFeedPubSubHubbub

ZendFeedPubSubHubbub is an implementation of the PubSubHubbub Core 0.2 Specification (Working Draft).
It offers implementations of a Pubsubhubbub Publisher and Subscriber suited to Zend Framework and other PHP
applications.

104.1 What is PubSubHubbub?
Pubsubhubbub is an open, simple web-scale pubsub protocol. A common use case to enable blogs (Publishers) to
“push” updates from their RSS or Atom feeds (Topics) to end Subscribers. These Subscribers will have subscribed to
the blog’s RSS or Atom feed via a Hub, a central server which is notified of any updates by the Publisher and which
then distributes these updates to all Subscribers. Any feed may advertise that it supports one or more Hubs using an
Atom namespaced link element with a rel attribute of “hub”.
Pubsubhubbub has garnered attention because it is a pubsub protocol which is easy to implement and which operates
over HTTP. Its philosophy is to replace the traditional model where blog feeds have been polled at regular intervals to
detect and retrieve updates. Depending on the frequency of polling, this can take a lot of time to propagate updates to
interested parties from planet aggregators to desktop readers. With a pubsub system in place, updates are not simply
polled by Subscribers, they are pushed to Subscribers, eliminating any delay. For this reason, Pubsubhubbub forms
part of what has been dubbed the real-time web.
The protocol does not exist in isolation. Pubsub systems have been around for a while, such as the familiar Jabber
Publish-Subscribe protocol, XEP-0060, or the less well known rssCloud (described in 2001). However these have
not achieved widespread adoption typically due to either their complexity, poor timing or lack of suitability for web
applications. rssCloud, which was recently revived as a response to the appearance of Pubsubhubbub, has also seen its
usage increase significantly though it lacks a formal specification and currently does not support Atom 1.0 feeds.
Perhaps surprisingly given its relative early age, Pubsubhubbub is already in use including in Google Reader, Feedburner, and there are plugins available for Wordpress blogs.

104.2 Architecture
ZendFeedPubSubHubbub implements two sides of the Pubsubhubbub 0.2 Specification: a Publisher and a
Subscriber. It does not currently implement a Hub Server though this is in progress for a future Zend Framework
release.

439
Zend Framework 2 Documentation, Release 2.2.5

A Publisher is responsible for notifying all supported Hubs (many can be supported to add redundancy to the system)
of any updates to its feeds, whether they be Atom or RSS based. This is achieved by pinging the supported Hub Servers
with the URL of the updated feed. In Pubsubhubbub terminology, any updatable resource capable of being subscribed
to is referred to as a Topic. Once a ping is received, the Hub will request the updated feed, process it for updated items,
and forward all updates to all Subscribers subscribed to that feed.
A Subscriber is any party or application which subscribes to one or more Hubs to receive updates from a Topic hosted
by a Publisher. The Subscriber never directly communicates with the Publisher since the Hub acts as an intermediary,
accepting subscriptions and sending updates to subscribed Subscribers. The Subscriber therefore communicates only
with the Hub, either to subscribe or unsubscribe to Topics, or when it receives updates from the Hub. This communication design (“Fat Pings”) effectively removes the possibility of a “Thundering Herd” issue. This occurs in a pubsub
system where the Hub merely informs Subscribers that an update is available, prompting all Subscribers to immediately retrieve the feed from the Publisher giving rise to a traffic spike. In Pubsubhubbub, the Hub distributes the actual
update in a “Fat Ping” so the Publisher is not subjected to any traffic spike.
ZendFeedPubSubHubbub implements Pubsubhubbub Publishers and Subscribers with the classes
ZendFeedPubSubHubbubPublisher and ZendFeedPubSubHubbubSubscriber. In addition, the Subscriber implementation may handle any feed updates forwarded from a Hub by using
ZendFeedPubSubHubbubSubscriberCallback. These classes, their use cases, and APIs are covered
in subsequent sections.

104.3 ZendFeedPubSubHubbubPublisher
In Pubsubhubbub, the Publisher is the party who publishes a live feed and frequently updates it with new content.
This may be a blog, an aggregator, or even a web service with a public feed based API. In order for these updates
to be pushed to Subscribers, the Publisher must notify all of its supported Hubs that an update has occurred using a
simple HTTP POST request containing the URI or the updated Topic (i.e the updated RSS or Atom feed). The Hub
will confirm receipt of the notification, fetch the updated feed, and forward any updates to any Subscribers who have
subscribed to that Hub for updates from the relevant feed.
By design, this means the Publisher has very little to do except send these Hub pings whenever its feeds change. As
a result, the Publisher implementation is extremely simple to use and requires very little work to setup and use when
feeds are updated.
ZendFeedPubSubHubbubPublisher implements a full Pubsubhubbub Publisher. Its setup for use is also
simple, requiring mainly that it is configured with the URI endpoint for all Hubs to be notified of updates, and the
URIs of all Topics to be included in the notifications.
The following example shows a Publisher notifying a collection of Hubs about updates to a pair of local RSS and Atom
feeds. The class retains a collection of errors which include the Hub URLs, so the notification can be re-attempted later
and/or logged if any notifications happen to fail. Each resulting error array also includes a “response” key containing
the related HTTP response object. In the event of any errors, it is strongly recommended to attempt the operation for
failed Hub Endpoints at least once more at a future time. This may require the use of either a scheduled task for this
purpose or a job queue though such extra steps are optional.
1
2
3
4
5
6
7
8
9
10

$publisher = new ZendFeedPubSubHubbubPublisher;
$publisher->addHubUrls(array(
’http://pubsubhubbub.appspot.com/’,
’http://hubbub.example.com’,
));
$publisher->addUpdatedTopicUrls(array(
’http://www.example.net/rss’,
’http://www.example.net/atom’,
));
$publisher->notifyAll();

11

440

Chapter 104. ZendFeedPubSubHubbub
Zend Framework 2 Documentation, Release 2.2.5

12
13
14
15
16
17
18
19

if (!$publisher->isSuccess()) {
// check for errors
$errors
= $publisher->getErrors();
$failedHubs = array();
foreach ($errors as $error) {
$failedHubs[] = $error[’hubUrl’];
}
}

20
21

// reschedule notifications for the failed Hubs in $failedHubs

If you prefer having more concrete control over the Publisher, the methods addHubUrls()
and addUpdatedTopicUrls() pass each array value to the singular addHubUrl() and
addUpdatedTopicUrl() public methods. There are also matching removeUpdatedTopicUrl() and
removeHubUrl() methods.
You can also skip setting Hub URIs, and notify each in turn using the notifyHub() method which accepts the URI
of a Hub endpoint as its only argument.
There are no other tasks to cover. The Publisher implementation is very simple since most of the feed processing and
distribution is handled by the selected Hubs. It is however important to detect errors and reschedule notifications as
soon as possible (with a reasonable maximum number of retries) to ensure notifications reach all Subscribers. In many
cases as a final alternative, Hubs may frequently poll your feeds to offer some additional tolerance for failures both in
terms of their own temporary downtime or Publisher errors or downtime.

104.4 ZendFeedPubSubHubbubSubscriber
In Pubsubhubbub, the Subscriber is the party who wishes to receive updates to any Topic (RSS or Atom feed). They
achieve this by subscribing to one or more of the Hubs advertised by that Topic, usually as a set of one or more Atom
1.0 links with a rel attribute of “hub”. The Hub from that point forward will send an Atom or RSS feed containing all
updates to that Subscriber’s Callback URL when it receives an update notification from the Publisher. In this way, the
Subscriber need never actually visit the original feed (though it’s still recommended at some level to ensure updates
are retrieved if ever a Hub goes offline). All subscription requests must contain the URI of the Topic being subscribed
and a Callback URL which the Hub will use to confirm the subscription and to forward updates.
The Subscriber therefore has two roles. To create and manage subscriptions, including subscribing for new Topics with
a Hub, unsubscribing (if necessary), and periodically renewing subscriptions since they may have a limited validity as
set by the Hub. This is handled by ZendFeedPubSubHubbubSubscriber.
The second role is to accept updates sent by a Hub to the Subscriber’s Callback URL, i.e. the URI the
Subscriber has assigned to handle updates. The Callback URL also handles events where the Hub contacts
the Subscriber to confirm all subscriptions and unsubscriptions. This is handled by using an instance of
ZendFeedPubSubHubbubSubscriberCallback when the Callback URL is accessed.
Important: ZendFeedPubSubHubbubSubscriber implements the Pubsubhubbub 0.2 Specification. As
this is a new specification version not all Hubs currently implement it. The new specification allows the Callback URL
to include a query string which is used by this class, but not supported by all Hubs. In the interests of maximising
compatibility it is therefore recommended that the query string component of the Subscriber Callback URI be presented
as a path element, i.e. recognised as a parameter in the route associated with the Callback URI and used by the
application’s Router.

104.4. ZendFeedPubSubHubbubSubscriber

441
Zend Framework 2 Documentation, Release 2.2.5

104.4.1 Subscribing and Unsubscribing
ZendFeedPubSubHubbubSubscriber implements a full Pubsubhubbub Subscriber capable of subscribing to, or unsubscribing from, any Topic via any Hub advertised by that Topic. It operates in conjunction with
ZendFeedPubSubHubbubSubscriberCallback which accepts requests from a Hub to confirm all subscription or unsubscription attempts (to prevent third-party misuse).
Any subscription (or unsubscription) requires the relevant information before proceeding, i.e. the URI of the Topic
(Atom or RSS feed) to be subscribed to for updates, and the URI of the endpoint for the Hub which will handle
the subscription and forwarding of the updates. The lifetime of a subscription may be determined by the Hub but
most Hubs should support automatic subscription refreshes by checking with the Subscriber. This is supported by
ZendFeedPubSubHubbubSubscriberCallback and requires no other work on your part. It is still
strongly recommended that you use the Hub sourced subscription time to live (ttl) to schedule the creation of new
subscriptions (the process is identical to that for any new subscription) to refresh it with the Hub. While it should not
be necessary per se, it covers cases where a Hub may not support automatic subscription refreshing and rules out Hub
errors for additional redundancy.
With the relevant information to hand, a subscription can be attempted as demonstrated below:
1

$storage = new ZendFeedPubSubHubbubModelSubscription;

2
3
4
5
6
7
8

$subscriber = new ZendFeedPubSubHubbubSubscriber;
$subscriber->setStorage($storage);
$subscriber->addHubUrl(’http://hubbub.example.com’);
$subscriber->setTopicUrl(’http://www.example.net/rss.xml’);
$subscriber->setCallbackUrl(’http://www.mydomain.com/hubbub/callback’);
$subscriber->subscribeAll();

In order to store subscriptions and offer access to this data for general use, the component requires a database (a
schema is provided later in this section). By default, it is assumed the table name is “subscription” and it utilises
ZendDbTableAbstract in the background meaning it will use the default adapter you have set for your application. You may also pass a specific custom ZendDbTableAbstract instance into the associated model
ZendFeedPubSubHubbubModelSubscription. This custom adapter may be as simple in intent as
changing the table name to use or as complex as you deem necessary.
While this Model is offered as a default ready-to-roll solution, you may create your own Model using any
other backend or database layer (e.g. Doctrine) so long as the resulting class implements the interface
ZendFeedPubSubHubbubModelSubscriptionInterface.
An example schema (MySQL) for a subscription table accessible by the provided model may look similar to:
1
2
3
4
5
6
7
8
9
10
11
12

CREATE TABLE IF NOT EXISTS ‘subscription‘ (
‘id‘ varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT ’’,
‘topic_url‘ varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
‘hub_url‘ varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
‘created_time‘ datetime DEFAULT NULL,
‘lease_seconds‘ bigint(20) DEFAULT NULL,
‘verify_token‘ varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
‘secret‘ varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
‘expiration_time‘ datetime DEFAULT NULL,
‘subscription_state‘ varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (‘id‘)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

Behind the scenes, the Subscriber above will send a request to the Hub endpoint containing the following parameters
(based on the previous example):

442

Chapter 104. ZendFeedPubSubHubbub
Zend Framework 2 Documentation, Release 2.2.5

Table 104.1: Subscription request parameters

PaValue
Explanation
rameter
hub.callback
http://www.mydomain.com/hubbub/callback?xhub.subscription=5536df06b5dcb966edab3a4c4d
The URI used by a Hub to contact the Subscriber and
either request confirmation of a (un)subscription
request or send updates from subscribed feeds. The
appended query string contains a custom parameter
(hence the xhub designation). It is a query string
parameter preserved by the Hub and resent with all
Subscriber requests. Its purpose is to allow the
Subscriber to identify and look up the subscription
associated with any Hub request in a backend storage
medium. This is a non=standard parameter used by
this component in preference to encoding a
subscription key in the URI path which is more
difficult to implement in a Zend Framework
application. Nevertheless, since not all Hubs support
query string parameters, we still strongly recommend
adding the subscription key as a path component in the
form
http://www.mydomain.com/hubbub/callback/5536df06b5
To accomplish this, it requires defining a route capable
of parsing out the final value of the key and then
retrieving the value and passing it to the Subscriber
Callback object. The value would be passed into the
method ZendPubSubHubbubSubscriberCallback::setSubscriptionKey(). A detailed example is
offered later.
hub.lease_seconds
2592000
The number of seconds for which the Subscriber
would like a new subscription to remain valid for (i.e.
a TTL). Hubs may enforce their own maximum
subscription period. All subscriptions should be
renewed by simply re-subscribing before the
subscription period ends to ensure continuity of
updates. Hubs should additionally attempt to
automatically refresh subscriptions before they expire
by contacting Subscribers (handled automatically by
the Callback class).
hub.mode
subscribe
Simple value indicating this is a subscription request.
Unsubscription requests would use the “unsubscribe”
value.
hub.topichttp://www.example.net/rss.xml
The URI of the topic (i.e. Atom or RSS feed) which
the Subscriber wishes to subscribe to for updates.
hub.verifyync
s
Indicates to the Hub the preferred mode of verifying
subscriptions or unsubscriptions. It is repeated twice
in order of preference. Technically this component
does not distinguish between the two modes and treats
both equally.
hub.verifysync
a
Indicates to the Hub the preferred mode of verifying
subscriptions or unsubscriptions. It is repeated twice
in order of preference. Technically this component
does not distinguish between the two modes and treats
both equally.
hub.verify_token
3065919804abA verification token returned to the Subscriber by the
104.4. ZendFeedPubSubHubbubSubscriber
443
caa7212ae89.879827871253878386
Hub when it is confirming a subscription or
unsubscription. Offers a measure of reliance that the
confirmation request originates from the correct Hub
to prevent misuse.
Zend Framework 2 Documentation, Release 2.2.5

You
can
modify
several
of
these
parameters
to
indicate
a
different
preference.
For
example,
you
can
set
a
different
lease
seconds
value
using
ZendFeedPubSubHubbubSubscriber::setLeaseSeconds() or show a preference for the async verify mode by using setPreferredVerificationMode(ZendFeedPubSubHubbubPubSubHubbub::VERIFICATION_
However the Hubs retain the capability to enforce their own preferences and for this reason the component is deliberately designed to work across almost any set of options with minimum end-user configuration required. Conventions
are great when they work!
Note:
While Hubs may require the use of a specific verification mode (both are supported by ZendFeedPubSubHubbub), you may indicate a specific preference using the
setPreferredVerificationMode() method. In “sync” (synchronous) mode, the Hub attempts to confirm a subscription as soon as it is received, and before responding to the subscription request. In “async”
(asynchronous) mode, the Hub will return a response to the subscription request immediately, and its verification
request may occur at a later time. Since ZendFeedPubSubHubbub implements the Subscriber verification role
as a separate callback class and requires the use of a backend storage medium, it actually supports both transparently
though in terms of end-user performance, asynchronous verification is very much preferred to eliminate the impact of
a poorly performing Hub tying up end-user server resources and connections for too long.
Unsubscribing from a Topic follows the exact same pattern as the previous example, with the exception that we should
call unsubscribeAll() instead. The parameters included are identical to a subscription request with the exception
that “hub.mode” is set to “unsubscribe”.
By default, a new instance of ZendPubSubHubbubSubscriber will attempt to use a database backed storage
medium which defaults to using the default ZendDb adapter with a table name of “subscription”. It is recommended
to set a custom storage solution where these defaults are not apt either by passing in a new Model supporting the
required interface or by passing a new instance of ZendDbTableAbstract to the default Model’s constructor
to change the used table name.

104.4.2 Handling Subscriber Callbacks
Whenever a subscription or unsubscription request is made, the Hub must verify the request by forwarding a new
verification request to the Callback URL set in the subscription or unsubscription parameters. To handle these Hub
requests, which will include all future communications containing Topic (feed) updates, the Callback URL should
trigger the execution of an instance of ZendFeedPubSubHubbubSubscriberCallback to handle the
request.
The Callback class should be configured to use the same storage medium as the Subscriber class. Using it is quite
simple since most of its work is performed internally.
1
2
3
4
5

$storage = new ZendFeedPubSubHubbubModelSubscription;
$callback = new ZendFeedPubSubHubbubSubscriberCallback;
$callback->setStorage($storage);
$callback->handle();
$callback->sendResponse();

6
7
8
9
10
11
12
13
14
15
16

/**
* Check if the callback resulting in the receipt of a feed update.
* Otherwise it was either a (un)sub verification request or invalid request.
* Typically we need do nothing other than add feed update handling - the rest
* is handled internally by the class.
*/
if ($callback->hasFeedUpdate()) {
$feedString = $callback->getFeedUpdate();
/**
* Process the feed update asynchronously to avoid a Hub timeout.

444

Chapter 104. ZendFeedPubSubHubbub
Zend Framework 2 Documentation, Release 2.2.5

*/

17
18

}

Note: It should be noted that ZendFeedPubSubHubbubSubscriberCallback may independently
parse any incoming query string and other parameters. This is necessary since PHP alters the structure and keys of a
query string when it is parsed into the $_GET or $_POST superglobals. For example, all duplicate keys are ignored
and periods are converted to underscores. Pubsubhubbub features both of these in the query strings it generates.

Important: It is essential that developers recognise that Hubs are only concerned with sending requests and receiving
a response which verifies its receipt. If a feed update is received, it should never be processed on the spot since this
leaves the Hub waiting for a response. Rather, any processing should be offloaded to another process or deferred until
after a response has been returned to the Hub. One symptom of a failure to promptly complete Hub requests is that
a Hub may continue to attempt delivery of the update or verification request leading to duplicated update attempts
being processed by the Subscriber. This appears problematic - but in reality a Hub may apply a timeout of just a few
seconds, and if no response is received within that time it may disconnect (assuming a delivery failure) and retry later.
Note that Hubs are expected to distribute vast volumes of updates so their resources are stretched - please do process
feeds asynchronously (e.g. in a separate process or a job queue or even a cron scheduled task) as much as possible.

104.4.3 Setting Up And Using A Callback URL Route
As noted earlier, the ZendFeedPubSubHubbubSubscriberCallback class receives the combined key
associated with any subscription from the Hub via one of two methods. The technically preferred method is to add
this key to the Callback URL employed by the Hub in all future requests using a query string parameter with the key
“xhub.subscription”. However, for historical reasons, primarily that this was not supported in Pubsubhubbub 0.1 (it
was recently added in 0.2 only), it is strongly recommended to use the most compatible means of adding this key to
the Callback URL by appending it to the URL‘s path.
Thus the URL http://www.example.com/callback?xhub.subscription=key would become
http://www.example.com/callback/key.
Since the query string method is the default in anticipation of a greater level of future support for the full 0.2 specification, this requires some additional work to implement.
The first step to make the ZendFeedPubSubHubbubSubscriberCallback class aware
of the path contained subscription key.
It’s manually injected therefore since it also requires
manually defining a route for this purpose.
This is achieved simply by called the method
ZendFeedPubSubHubbubSubscriberCallback::setSubscriptionKey() with the parameter being the key value available from the Router. The example below demonstrates this using a Zend Framework
controller.
1

use ZendMvcControllerAbstractActionController;

2
3
4

class CallbackController extends AbstractActionController
{

5
6
7
8
9
10
11
12
13

public function indexAction()
{
$storage = new ZendFeedPubSubHubbubModelSubscription;
$callback = new ZendFeedPubSubHubbubSubscriberCallback;
$callback->setStorage($storage);
/**
* Inject subscription key parsing from URL path using
* a parameter from Router.

104.4. ZendFeedPubSubHubbubSubscriber

445
Zend Framework 2 Documentation, Release 2.2.5

*/
$subscriptionKey = $this->params()->fromRoute(’subkey’);
$callback->setSubscriptionKey($subscriptionKey);
$callback->handle();
$callback->sendResponse();

14
15
16
17
18
19

/**
* Check if the callback resulting in the receipt of a feed update.
* Otherwise it was either a (un)sub verification request or invalid
* request. Typically we need do nothing other than add feed update
* handling - the rest is handled internally by the class.
*/
if ($callback->hasFeedUpdate()) {
$feedString = $callback->getFeedUpdate();
/**
* Process the feed update asynchronously to avoid a Hub timeout.
*/
}

20
21
22
23
24
25
26
27
28
29
30
31

}

32
33
34

}

Actually adding the route which would map the path-appended key to a parameter for retrieval from a controller can
be accomplished using a Route like in the example below.
1
2
3
4
5
6
7
8
9
10
11

// Callback Route to enable appending a PuSH Subscription’s lookup key
$route = ZendMvcRouterHttpSegment::factory(array(
’route’ => ’/callback/:subkey’,
’constraints’ => array(
’subkey’ => ’[a-z0-9]+’
),
’defaults’ => array(
’controller’ => ’application-index’,
’action’ => ’index’
)
));

446

Chapter 104. ZendFeedPubSubHubbub
CHAPTER 105

ZendFileClassFileLocator

105.1 Overview
TODO

105.2 Available Methods
TODO

105.3 Examples
TODO

447
Zend Framework 2 Documentation, Release 2.2.5

448

Chapter 105. ZendFileClassFileLocator
CHAPTER 106

Introduction to ZendFilter

The ZendFilter component provides a set of commonly needed data filters. It also provides a simple filter
chaining mechanism by which multiple filters may be applied to a single datum in a user-defined order.

106.1 What is a filter?
In the physical world, a filter is typically used for removing unwanted portions of input, and the desired portion of the
input passes through as filter output (e.g., coffee). In such scenarios, a filter is an operator that produces a subset of the
input. This type of filtering is useful for web applications - removing illegal input, trimming unnecessary white space,
etc.
This basic definition of a filter may be extended to include generalized transformations upon input. A common transformation applied in web applications is the escaping of HTML entities. For example, if a form field is automatically
populated with untrusted input (e.g., from a web browser), this value should either be free of HTML entities or contain only escaped HTML entities, in order to prevent undesired behavior and security vulnerabilities. To meet this
requirement, HTML entities that appear in the input must either be removed or escaped. Of course, which approach
is more appropriate depends on the situation. A filter that removes the HTML entities operates within the scope of
the first definition of filter - an operator that produces a subset of the input. A filter that escapes the HTML entities,
however, transforms the input (e.g., “&” is transformed to “&amp;”). Supporting such use cases for web developers is
important, and “to filter,” in the context of using ZendFilter, means to perform some transformations upon input
data.

106.2 Basic usage of filters
Having this filter definition established provides the foundation for ZendFilterFilterInterface, which
requires a single method named filter() to be implemented by a filter class.
Following is a basic example of using a filter upon two input data, the ampersand (&) and double quote (“) characters:
1

$htmlEntities = new ZendFilterHtmlEntities();

2
3
4

echo $htmlEntities->filter(’&’); // &amp;
echo $htmlEntities->filter(’"’); // &quot;

Also, if a Filter inherits from ZendFilterAbstractFilter (just like all out-of-the-box Filters) you can also
use them as such:
449
Zend Framework 2 Documentation, Release 2.2.5

1

$strtolower = new ZendFilterStringToLower;

2
3
4

echo $strtolower(’I LOVE ZF2!’); // i love zf2!
$zf2love = $strtolower(’I LOVE ZF2!’);

450

Chapter 106. Introduction to ZendFilter
CHAPTER 107

Using the StaticFilter

If it is inconvenient to load a given filter class and create an instance of the filter, you can use StaticFilter with
it’s method execute() as an alternative invocation style. The first argument of this method is a data input value,
that you would pass to the filter() method. The second argument is a string, which corresponds to the basename
of the filter class, relative to the ZendFilter namespace. The execute() method automatically loads the class,
creates an instance, and applies the filter() method to the data input.
1

echo StaticFilter::execute(’&’, ’HtmlEntities’);

You can also pass an array of constructor arguments, if they are needed for the filter class.
1
2
3

echo StaticFilter::execute(’"’,
’HtmlEntities’,
array(’quotestyle’ => ENT_QUOTES));

The static usage can be convenient for invoking a filter ad hoc, but if you have the need to run a filter for multiple
inputs, it’s more efficient to follow the first example above, creating an instance of the filter object and calling its
filter() method.
Also, the FilterChain class allows you to instantiate and run multiple filter and validator classes on demand to
process sets of input data. See FilterChain.
You can set and receive the FilterPluginManager for the StaticFilter to amend the standard filter classes.
1
2
3

$pluginManager = StaticFilter::getPluginManager()->setInvokableClass(
’myNewFilter’, ’MyCustomFilterMyNewFilter’
);

4
5

StaticFilter::setPluginManager(new MyFilterPluginManager());

This is useful when adding custom filters to be used by the StaticFilter.

107.1 Double filtering
When using two filters after each other you have to keep in mind that it is often not possible to get the original output
by using the opposite filter. Take the following example:
1

$original = "my_original_content";

2

451
Zend Framework 2 Documentation, Release 2.2.5

3
4
5

// Attach a filter
$filter
= new ZendFilterWordUnderscoreToCamelCase();
$filtered = $filter->filter($original);

6
7
8
9

// Use it’s opposite
$filter2 = new ZendFilterWordCamelCaseToUnderscore();
$filtered = $filter2->filter($filtered)

The above code example could lead to the impression that you will get the original output after the second filter has
been applied. But thinking logically this is not the case. After applying the first filter my_original_content will be
changed to MyOriginalContent. But after applying the second filter the result is My_Original_Content.
As you can see it is not always possible to get the original output by using a filter which seems to be the opposite. It
depends on the filter and also on the given input.

452

Chapter 107. Using the StaticFilter
CHAPTER 108

Standard Filter Classes

Zend Framework comes with a standard set of filters, which are ready for you to use.

108.1 Alnum
The Alnum filter can be used to return only alphabetic characters and digits in the unicode “letter” and “number”
categories, respectively. All other characters are suppressed.
Supported Options for Alnum Filter

The following options are supported for Alnum:
Alnum([ boolean $allowWhiteSpace [, string $locale ]])
• $allowWhiteSpace: If set to true then whitespace characters are allowed. Otherwise they are suppressed.
Default is “false” (whitespace is not allowed).
Methods for getting/setting the allowWhiteSpace option are also available: getAllowWhiteSpace() and
setAllowWhiteSpace()
• $locale: The locale string used in identifying the characters to filter (locale name, e.g. en_US). If unset, it
will use the default locale (Locale::getDefault()).
Methods for getting/setting the locale are also available: getLocale() and setLocale()
Alnum Filter Usage

1
2
3
4

// Default settings, deny whitespace
$filter = new ZendI18nFilterAlnum();
echo $filter->filter("This is (my) content: 123");
// Returns "Thisismycontent123"

5
6
7
8
9

// First param in constructor is $allowWhiteSpace
$filter = new ZendI18nFilterAlnum(true);
echo $filter->filter("This is (my) content: 123");
// Returns "This is my content 123"

453
Zend Framework 2 Documentation, Release 2.2.5

Note: Alnum works on almost all languages, except: Chinese, Japanese and Korean. Within these languages the
english alphabet is used instead of the characters from these languages. The language itself is detected using the
Locale.

108.2 Alpha
The Alpha filter can be used to return only alphabetic characters in the unicode “letter” category. All other characters
are suppressed.
Supported Options for Alpha Filter

The following options are supported for Alpha:
Alpha([ boolean $allowWhiteSpace [, string $locale ]])
• $allowWhiteSpace: If set to true then whitespace characters are allowed. Otherwise they are suppressed.
Default is “false” (whitespace is not allowed).
Methods for getting/setting the allowWhiteSpace option are also available: getAllowWhiteSpace() and
setAllowWhiteSpace()
• $locale: The locale string used in identifying the characters to filter (locale name, e.g. en_US). If unset, it
will use the default locale (Locale::getDefault()).
Methods for getting/setting the locale are also available: getLocale() and setLocale()
Alpha Filter Usage

1
2
3
4

// Default settings, deny whitespace
$filter = new ZendI18nFilterAlpha();
echo $filter->filter("This is (my) content: 123");
// Returns "Thisismycontent"

5
6
7
8
9

// Allow whitespace
$filter = new ZendI18nFilterAlpha(true);
echo $filter->filter("This is (my) content: 123");
// Returns "This is my content "

Note: Alpha works on almost all languages, except: Chinese, Japanese and Korean. Within these languages the
english alphabet is used instead of the characters from these languages. The language itself is detected using the
Locale.

108.3 BaseName
ZendFilterBaseName allows you to filter a string which contains the path to a file and it will return the base
name of this file.

454

Chapter 108. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.2.5

Supported Options

There are no additional options for ZendFilterBaseName.
Basic Usage

A basic example of usage is below:
1

$filter = new ZendFilterBaseName();

2
3

print $filter->filter(’/vol/tmp/filename’);

This will return ‘filename’.
1

$filter = new ZendFilterBaseName();

2
3

print $filter->filter(’/vol/tmp/filename.txt’);

This will return ‘filename.txt‘.

108.4 Boolean
This filter changes a given input to be a BOOLEAN value. This is often useful when working with databases or when
processing form values.
Supported Options

The following options are supported for ZendFilterBoolean:
• casting: When this option is set to TRUE then any given input will be casted to boolean. This option defaults to
TRUE.
• locale: This option sets the locale which will be used to detect localized input.
• type: The type option sets the boolean type which should be used. Read the following for details.
Default Behavior

By default, this filter works by casting the input to a BOOLEAN value; in other words, it operates in a similar fashion
to calling (boolean) $value.
1
2
3
4

$filter = new ZendFilterBoolean();
$value = ’’;
$result = $filter->filter($value);
// returns false

This means that without providing any configuration, ZendFilterBoolean accepts all input types and returns
a BOOLEAN just as you would get by type casting to BOOLEAN.

108.4. Boolean

455
Zend Framework 2 Documentation, Release 2.2.5

Changing the Default Behavior

Sometimes casting with (boolean) will not suffice. ZendFilterBoolean allows you to configure specific
types to convert, as well as which to omit.
The following types can be handled:
• boolean: Returns a boolean value as is.
• integer: Converts an integer 0 value to FALSE.
• float: Converts a float 0.0 value to FALSE.
• string: Converts an empty string ‘’ to FALSE.
• zero: Converts a string containing the single character zero (‘0’) to FALSE.
• empty_array: Converts an empty array to FALSE.
• null: Converts a NULL value to FALSE.
• php: Converts values according to PHP when casting them to BOOLEAN.
• false_string: Converts a string containing the word “false” to a boolean FALSE.
• yes: Converts a localized string which contains the word “no” to FALSE.
• all: Converts all above types to BOOLEAN.
All other given values will return TRUE by default.
There are several ways to select which of the above types are filtered. You can give one or multiple types and add
them, you can give an array, you can use constants, or you can give a textual string. See the following examples:
1
2

// converts 0 to false
$filter = new ZendFilterBoolean(ZendFilterBoolean::INTEGER);

3
4
5
6
7

// converts 0 and ’0’ to false
$filter = new ZendFilterBoolean(
ZendFilterBoolean::INTEGER + ZendFilterBoolean::ZERO
);

8
9
10
11
12
13
14
15

// converts 0 and ’0’ to false
$filter = new ZendFilterBoolean(array(
’type’ => array(
ZendFilterBoolean::INTEGER,
ZendFilterBoolean::ZERO,
),
));

16
17
18
19
20
21
22
23

// converts 0 and ’0’ to false
$filter = new ZendFilterBoolean(array(
’type’ => array(
’integer’,
’zero’,
),
));

You can also give an instance of ZendConfigConfig to set the desired types. To set types after instantiation,
use the setType() method.

456

Chapter 108. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.2.5

Localized Booleans

As mentioned previously, ZendFilterBoolean can also recognise localized “yes” and “no” strings. This means
that you can ask your customer in a form for “yes” or “no” within his native language and ZendFilterBoolean
will convert the response to the appropriate boolean value.
To set the desired locale, you can either use the locale option, or the method setLocale().
1
2
3
4

$filter = new ZendFilterBoolean(array(
’type’
=> ZendFilterBoolean::ALL,
’locale’ => ’de’,
));

5
6
7

// returns false
echo $filter->filter(’nein’);

8
9

$filter->setLocale(’en’);

10
11
12

// returns true
$filter->filter(’yes’);

Disable Casting

Sometimes it is necessary to recognise only TRUE or FALSE and return all other values without changes.
ZendFilterBoolean allows you to do this by setting the casting option to FALSE.
In this case ZendFilterBoolean will work as described in the following table, which shows which values
return TRUE or FALSE. All other given values are returned without change when casting is set to FALSE
Table 108.1: Usage without casting
Type
ZendFilterBoolean::BOOLEAN
ZendFilterBoolean::INTEGER
ZendFilterBoolean::FLOAT
ZendFilterBoolean::STRING
ZendFilterBoolean::ZERO
ZendFilterBoolean::EMPTY_ARRAY
ZendFilterBoolean::NULL
ZendFilterBoolean::FALSE_STRING
ZendFilterBoolean::YES

True
TRUE
0
0.0
“”
“0”
array()
NULL
“false” (case independently)
localized “yes” (case independently)

False
FALSE
1
1.0
“1”

“true” (case independently)
localized “no” (case independently)

The following example shows the behaviour when changing the casting option:
1
2
3
4

$filter = new ZendFilterBoolean(array(
’type’
=> ZendFilterBoolean::ALL,
’casting’ => false,
));

5
6
7

// returns false
echo $filter->filter(0);

8
9
10

// returns true
echo $filter->filter(1);

11
12
13

// returns the value
echo $filter->filter(2);

108.4. Boolean

457
Zend Framework 2 Documentation, Release 2.2.5

108.5 Callback
This filter allows you to use own methods in conjunction with ZendFilter. You don’t have to create a new filter
when you already have a method which does the job.
Supported Options

The following options are supported for ZendFilterCallback:
• callback: This sets the callback which should be used.
• options: This property sets the options which are used when the callback is processed.
Basic Usage

The usage of this filter is quite simple. Let’s expect we want to create a filter which reverses a string.
1

$filter = new ZendFilterCallback(’strrev’);

2
3
4

print $filter->filter(’Hello!’);
// returns "!olleH"

As you can see it’s really simple to use a callback to define a own filter. It is also possible to use a method, which is
defined within a class, by giving an array as callback.
1
2
3
4
5

// Our classdefinition
class MyClass
{
public function Reverse($param);
}

6
7
8
9

// The filter definition
$filter = new ZendFilterCallback(array(’MyClass’, ’Reverse’));
print $filter->filter(’Hello!’);

To get the actual set callback use getCallback() and to set another callback use setCallback().
Note: Possible exceptions
You should note that defining a callback method which can not be called will raise an exception.

Default Parameters Within a Callback

It is also possible to define default parameters, which are given to the called method as array when the filter is executed.
This array will be concatenated with the value which will be filtered.
1
2
3
4
5
6
7

$filter = new ZendFilterCallback(
array(
’callback’ => ’MyMethod’,
’options’ => array(’key’ => ’param1’, ’key2’ => ’param2’)
)
);
$filter->filter(array(’value’ => ’Hello’));

458

Chapter 108. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.2.5

When you would call the above method definition manually it would look like this:
1

$value = MyMethod(’Hello’, ’param1’, ’param2’);

108.6 Compress and Decompress
These two filters are capable of compressing and decompressing strings, files, and directories.
Supported Options

The following options are supported for ZendFilterCompress and ZendFilterDecompress:
• adapter: The compression adapter which should be used. It defaults to Gz.
• options: Additional options which are given to the adapter at initiation. Each adapter supports it’s own options.
Supported Compression Adapters

The following compression formats are supported by their own adapter:
• Bz2
• Gz
• Lzf
• Rar
• Tar
• Zip
Each compression format has different capabilities as described below. All compression filters may be used in approximately the same ways, and differ primarily in the options available and the type of compression they offer (both
algorithmically as well as string vs. file vs. directory)
Generic Handling

To create a compression filter you need to select the compression format you want to use. The following description
takes the Bz2 adapter. Details for all other adapters are described after this section.
The two filters are basically identical, in that they utilize the same backends. ZendFilterCompress should
be used when you wish to compress items, and ZendFilterDecompress should be used when you wish to
decompress items.
For instance, if we want to compress a string, we have to initiate ZendFilterCompress and indicate the desired
adapter.
1

$filter = new ZendFilterCompress(’Bz2’);

To use a different adapter, you simply specify it to the constructor.
You may also provide an array of options or a Traversable object. If you do, provide minimally the key “adapter”, and
then either the key “options” or “adapterOptions” (which should be an array of options to provide to the adapter on
instantiation).

108.6. Compress and Decompress

459
Zend Framework 2 Documentation, Release 2.2.5

1
2
3
4
5
6

$filter = new ZendFilterCompress(array(
’adapter’ => ’Bz2’,
’options’ => array(
’blocksize’ => 8,
),
));

Note: Default compression Adapter
When no compression adapter is given, then the Gz adapter will be used.
Almost the same usage is we want to decompress a string. We just have to use the decompression filter in this case.
1

$filter = new ZendFilterDecompress(’Bz2’);

To get the compressed string, we have to give the original string. The filtered value is the compressed version of the
original string.
1
2
3

$filter
= new ZendFilterCompress(’Bz2’);
$compressed = $filter->filter(’Uncompressed string’);
// Returns the compressed string

Decompression works the same way.
1
2
3

$filter
= new ZendFilterDecompress(’Bz2’);
$compressed = $filter->filter(’Compressed string’);
// Returns the uncompressed string

Note: Note on string compression
Not all adapters support string compression. Compression formats like Rar can only handle files and directories. For
details, consult the section for the adapter you wish to use.

Creating an Archive

Creating an archive file works almost the same as compressing a string. However, in this case we need an additional
parameter which holds the name of the archive we want to create.
1
2
3
4
5
6
7
8

$filter
= new ZendFilterCompress(array(
’adapter’ => ’Bz2’,
’options’ => array(
’archive’ => ’filename.bz2’,
),
));
$compressed = $filter->filter(’Uncompressed string’);
// Returns true on success and creates the archive file

In the above example the uncompressed string is compressed, and is then written into the given archive file.
Note: Existing archives will be overwritten
The content of any existing file will be overwritten when the given filename of the archive already exists.
When you want to compress a file, then you must give the name of the file with its path.

460

Chapter 108. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.2.5

1
2
3
4
5
6
7
8

$filter
= new ZendFilterCompress(array(
’adapter’ => ’Bz2’,
’options’ => array(
’archive’ => ’filename.bz2’
),
));
$compressed = $filter->filter(’C:tempcompressme.txt’);
// Returns true on success and creates the archive file

You may also specify a directory instead of a filename. In this case the whole directory with all its files and subdirectories will be compressed into the archive.
1
2
3
4
5
6
7
8

$filter
= new ZendFilterCompress(array(
’adapter’ => ’Bz2’,
’options’ => array(
’archive’ => ’filename.bz2’
),
));
$compressed = $filter->filter(’C:tempsomedir’);
// Returns true on success and creates the archive file

Note: Do not compress large or base directories
You should never compress large or base directories like a complete partition. Compressing a complete partition is a
very time consuming task which can lead to massive problems on your server when there is not enough space or your
script takes too much time.

Decompressing an Archive

Decompressing an archive file works almost like compressing it. You must specify either the archive parameter, or
give the filename of the archive when you decompress the file.
1
2
3

$filter
= new ZendFilterDecompress(’Bz2’);
$decompressed = $filter->filter(’filename.bz2’);
// Returns true on success and decompresses the archive file

Some adapters support decompressing the archive into another subdirectory. In this case you can set the target
parameter.
1
2
3
4
5
6
7
8
9

$filter
= new ZendFilterDecompress(array(
’adapter’ => ’Zip’,
’options’ => array(
’target’ => ’C:temp’,
)
));
$decompressed = $filter->filter(’filename.zip’);
// Returns true on success and decompresses the archive file
// into the given target directory

Note: Directories to extract to must exist
When you want to decompress an archive into a directory, then that directory must exist.

108.6. Compress and Decompress

461
Zend Framework 2 Documentation, Release 2.2.5

Bz2 Adapter

The Bz2 Adapter can compress and decompress:
• Strings
• Files
• Directories
This adapter makes use of PHP‘s Bz2 extension.
To customize compression, this adapter supports the following options:
• Archive: This parameter sets the archive file which should be used or created.
• Blocksize: This parameter sets the blocksize to use. It can be from ‘0’ to ‘9’. The default value is ‘4’.
All options can be set at instantiation or by using a related method. For example, the related methods for ‘Blocksize’
are getBlocksize() and setBlocksize(). You can also use the setOptions() method which accepts all
options as array.
Gz Adapter

The Gz Adapter can compress and decompress:
• Strings
• Files
• Directories
This adapter makes use of PHP‘s Zlib extension.
To customize the compression this adapter supports the following options:
• Archive: This parameter sets the archive file which should be used or created.
• Level: This compression level to use. It can be from ‘0’ to ‘9’. The default value is ‘9’.
• Mode: There are two supported modes. ‘compress’ and ‘deflate’. The default value is ‘compress’.
All options can be set at initiation or by using a related method. For example, the related methods for ‘Level’ are
getLevel() and setLevel(). You can also use the setOptions() method which accepts all options as array.

Lzf Adapter

The Lzf Adapter can compress and decompress:
• Strings
Note: Lzf supports only strings
The Lzf adapter can not handle files and directories.
This adapter makes use of PHP‘s Lzf extension.
There are no options available to customize this adapter.

462

Chapter 108. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.2.5

Rar Adapter

The Rar Adapter can compress and decompress:
• Files
• Directories
Note: Rar does not support strings
The Rar Adapter can not handle strings.
This adapter makes use of PHP‘s Rar extension.
Note: Rar compression not supported
Due to restrictions with the Rar compression format, there is no compression available for free. When you want to
compress files into a new Rar archive, you must provide a callback to the adapter that can invoke a Rar compression
program.
To customize the compression this adapter supports the following options:
• Archive: This parameter sets the archive file which should be used or created.
• Callback: A callback which provides compression support to this adapter.
• Password: The password which has to be used for decompression.
• Target: The target where the decompressed files will be written to.
All options can be set at instantiation or by using a related method. For example, the related methods for ‘Target’ are
getTarget() and setTarget(). You can also use the setOptions() method which accepts all options as
array.
Tar Adapter

The Tar Adapter can compress and decompress:
• Files
• Directories
Note: Tar does not support strings
The Tar Adapter can not handle strings.
This adapter makes use of PEAR‘s Archive_Tar component.
To customize the compression this adapter supports the following options:
• Archive: This parameter sets the archive file which should be used or created.
• Mode: A mode to use for compression. Supported are either ‘NULL‘ which means no compression at all, ‘Gz’
which makes use of PHP‘s Zlib extension and ‘Bz2’ which makes use of PHP‘s Bz2 extension. The default
value is ‘NULL‘.
• Target: The target where the decompressed files will be written to.

108.6. Compress and Decompress

463
Zend Framework 2 Documentation, Release 2.2.5

All options can be set at instantiation or by using a related method. For example, the related methods for ‘Target’ are
getTarget() and setTarget(). You can also use the setOptions() method which accepts all options as
array.
Note: Directory usage
When compressing directories with Tar then the complete file path is used. This means that created Tar files will not
only have the subdirectory but the complete path for the compressed file.

Zip Adapter

The Zip Adapter can compress and decompress:
• Strings
• Files
• Directories
Note: Zip does not support string decompression
The Zip Adapter can not handle decompression to a string; decompression will always be written to a file.
This adapter makes use of PHP‘s Zip extension.
To customize the compression this adapter supports the following options:
• Archive: This parameter sets the archive file which should be used or created.
• Target: The target where the decompressed files will be written to.
All options can be set at instantiation or by using a related method. For example, the related methods for ‘Target’ are
getTarget() and setTarget(). You can also use the setOptions() method which accepts all options as
array.

108.7 Digits
Returns the string $value, removing all but digits.
Supported Options

There are no additional options for ZendFilterDigits.
Basic Usage

A basic example of usage is below:
1

$filter = new ZendFilterDigits();

2
3

print $filter->filter(’October 2012’);

This returns “2012”.

464

Chapter 108. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.2.5

1

$filter = new ZendFilterDigits();

2
3

print $filter->filter(’HTML 5 for Dummies’);

This returns “5”.

108.8 Dir
Given a string containing a path to a file, this function will return the name of the directory.
Supported Options

There are no additional options for ZendFilterDir.
Basic Usage

A basic example of usage is below:
1

$filter = new ZendFilterDir();

2
3

print $filter->filter(’/etc/passwd’);

This returns “/etc”.
1

$filter = new ZendFilterDir();

2
3

print $filter->filter(’C:/Temp/x’);

This returns “C:/Temp”.

108.9 Encrypt and Decrypt
These filters allow to encrypt and decrypt any given string. Therefor they make use of Adapters. Actually there are
adapters for the ZendCryptBlockCipher class and the OpenSSL extension of PHP.
Supported Options

The following options are supported for ZendFilterEncrypt and ZendFilterDecrypt:
• adapter: This sets the encryption adapter which should be used
• algorithm: Only BlockCipher. The algorithm which has to be used by the adapter
ZendCryptSymmetricMcrypt. It should be one of the algorithm ciphers supported by
ZendCryptSymmetricMcrypt (see the getSupportedAlgorithms() method). If not set
it defaults to aes, the Advanced Encryption Standard (see ZendCryptBlockCipher for more details).
• compression: If the encrypted value should be compressed. Default is no compression.
• envelope: Only OpenSSL. The encrypted envelope key from the user who encrypted the content. You can
either provide the path and filename of the key file, or just the content of the key file itself. When the package
option has been set, then you can omit this parameter.
108.8. Dir

465
Zend Framework 2 Documentation, Release 2.2.5

• key: Only BlockCipher. The encryption key with which the input will be encrypted. You need the same key
for decryption.
• mode: Only BlockCipher. The encryption mode which has to be used. It should be one of the modes which
can be found under PHP’s mcrypt modes. If not set it defaults to ‘cbc’.
• mode_directory: Only BlockCipher. The directory where the mode can be found. If not set it defaults to
the path set within the Mcrypt extension.
• package: Only OpenSSL. If the envelope key should be packed with the encrypted value. Default is FALSE.
• private: Only OpenSSL. Your private key which will be used for encrypting the content. Also the private key
can be either a filename with path of the key file, or just the content of the key file itself.
• public: Only OpenSSL. The public key of the user whom you want to provide the encrypted content. You can
give multiple public keys by using an array. You can either provide the path and filename of the key file, or just
the content of the key file itself.
• vector: Only BlockCipher. The initialization vector which shall be used. If not set it will be a random
vector.
Adapter Usage

As these two encryption methodologies work completely different, also the usage of the adapters differ. You have to
select the adapter you want to use when initiating the filter.
1
2

// Use the BlockCipher adapter
$filter1 = new ZendFilterEncrypt(array(’adapter’ => ’BlockCipher’));

3
4
5

// Use the OpenSSL adapter
$filter2 = new ZendFilterEncrypt(array(’adapter’ => ’openssl’));

To set another adapter you can also use setAdapter(), and the getAdapter() method to receive the actual set
adapter.
1
2
3

// Use the OpenSSL adapter
$filter = new ZendFilterEncrypt();
$filter->setAdapter(’openssl’);

Note: When you do not supply the adapter option or do not use setAdapter(), then the BlockCipher
adapter will be used per default.

Encryption with BlockCipher

To encrypt a string using the BlockCipher you have to specify the encryption key using the setKey() method or
passing it during the constructor.
1
2
3

// Use the default AES encryption algorithm
$filter = new ZendFilterEncrypt(array(’adapter’ => ’BlockCipher’));
$filter->setKey(’encryption key’);

4
5
6
7
8
9

// or
// $filter = new ZendFilterEncrypt(array(
//
’adapter’ => ’BlockCipher’,
//
’key’
=> ’encryption key’
// ));

466

Chapter 108. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.2.5

10
11
12

$encrypted = $filter->filter(’text to be encrypted’);
printf ("Encrypted text: %sn", $encrypted);

You can get and set the encryption values also afterwards with the getEncryption() and setEncryption()
methods.
1
2
3
4

// Use the default AES encryption algorithm
$filter = new ZendFilterEncrypt(array(’adapter’ => ’BlockCipher’));
$filter->setKey(’encryption key’);
var_dump($filter->getEncryption());

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

// Will print:
//array(4) {
// ["key_iteration"]=>
// int(5000)
// ["algorithm"]=>
// string(3) "aes"
// ["hash"]=>
// string(6) "sha256"
// ["key"]=>
// string(14) "encryption key"
//}

Note: The BlockCipher adapter uses the Mcrypt PHP extension by default. That means you will need to install
the Mcrypt module in your PHP environment.
If you don’t specify an initialization Vector (salt or iv), the BlockCipher will generate a random value during each
encryption. If you try to execute the following code the output will be always different (note that even if the output is
always different you can decrypt it using the same key).
1
2

$key = ’encryption key’;
$text = ’message to encrypt’;

3
4
5
6
7
8
9

// use the default adapter that is BlockCipher
$filter = new ZendFilterEncrypt();
$filter->setKey(’encryption key’);
for ($i=0; $i < 10; $i++) {
printf("%d) %sn", $i, $filter->filter($text));
}

If you want to obtain the same output you need to specify a fixed Vector, using the setVector() method. This script will
produce always the same encryption output.
1
2
3
4
5

// use the default adapter that is BlockCipher
$filter = new ZendFilterEncrypt();
$filter->setKey(’encryption key’);
$filter->setVector(’12345678901234567890’);
printf("%sn", $filter->filter(’message’));

6
7
8

// output:
// 04636a6cb8276fad0787a2e187803b6557f77825d5ca6ed4392be702b9754bb3MTIzNDU2Nzg5MDEyMzQ1NgZ+zPwTGpV6gQ

Note: For a security reason it’s always better to use a different Vector on each encryption. We suggest to use the
setVector() method only if you really need it.

108.9. Encrypt and Decrypt

467
Zend Framework 2 Documentation, Release 2.2.5

Decryption with BlockCipher

For decrypting content which was previously encrypted with BlockCipher you need to have the options with which
the encryption has been called.
If you used only the encryption key, you can just use it to decrypt the content. As soon as you have provided all options
decryption is as simple as encryption.
1
2
3
4
5

$content = ’04636a6cb8276fad0787a2e187803b6557f77825d5ca6ed4392be702b9754bb3MTIzNDU2Nzg5MDEyMzQ1NgZ+z
// use the default adapter that is BlockCipher
$filter = new ZendFilterDecrypt();
$filter->setKey(’encryption key’);
printf("Decrypt: %sn", $filter->filter($content));

6
7
8

// output:
// Decrypt: message

Note that even if we did not specify the same Vector, the BlockCipher is able to decrypt the message because the
Vector is stored in the encryption string itself (note that the Vector can be stored in plaintext, it is not a secret, the
Vector is only used to improve the randomness of the encryption algorithm).
Note: You should also note that all settings which be checked when you create the instance or when you call
setEncryption().

Encryption with OpenSSL

When you have installed the OpenSSL extension you can use the OpenSSL adapter. You can get or set the public
keys also afterwards with the getPublicKey() and setPublicKey() methods. The private key can also be get
and set with the related getPrivateKey() and setPrivateKey() methods.
1
2
3
4
5

// Use openssl and provide a private key
$filter = new ZendFilterEncrypt(array(
’adapter’ => ’openssl’,
’private’ => ’/path/to/mykey/private.pem’
));

6
7
8
9
10
11

// of course you can also give the public keys at initiation
$filter->setPublicKey(array(
’/public/key/path/first.pem’,
’/public/key/path/second.pem’
));

Note: Note that the OpenSSL adapter will not work when you do not provide valid keys.
When you want to encode also the keys, then you have to provide a passphrase with the setPassphrase() method.
When you want to decode content which was encoded with a passphrase you will not only need the public key, but
also the passphrase to decode the encrypted key.
1
2
3
4
5

// Use openssl and provide a private key
$filter = new ZendFilterEncrypt(array(
’adapter’ => ’openssl’,
’private’ => ’/path/to/mykey/private.pem’
));

6

468

Chapter 108. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.2.5

7
8
9
10
11
12

// of course you can also give the public keys at initiation
$filter->setPublicKey(array(
’/public/key/path/first.pem’,
’/public/key/path/second.pem’
));
$filter->setPassphrase(’mypassphrase’);

At last, when you use OpenSSL you need to give the receiver the encrypted content, the passphrase when have provided
one, and the envelope keys for decryption.
This means for you, that you have to get the envelope keys after the encryption with the getEnvelopeKey()
method.
So our complete example for encrypting content with OpenSSL look like this.
1
2
3
4
5

// Use openssl and provide a private key
$filter = new ZendFilterEncrypt(array(
’adapter’ => ’openssl’,
’private’ => ’/path/to/mykey/private.pem’
));

6
7
8
9
10
11
12

// of course you can also give the public keys at initiation
$filter->setPublicKey(array(
’/public/key/path/first.pem’,
’/public/key/path/second.pem’
));
$filter->setPassphrase(’mypassphrase’);

13
14
15
16

$encrypted = $filter->filter(’text_to_be_encoded’);
$envelope = $filter->getEnvelopeKey();
print $encrypted;

17
18

// For decryption look at the Decrypt filter

Simplified usage with Openssl

As seen before, you need to get the envelope key to be able to decrypt the previous encrypted value. This can be very
annoying when you work with multiple values.
To have a simplified usage you can set the package option to TRUE. The default value is FALSE.
1
2
3
4
5
6
7

// Use openssl and provide a private key
$filter = new ZendFilterEncrypt(array(
’adapter’ => ’openssl’,
’private’ => ’/path/to/mykey/private.pem’,
’public’ => ’/public/key/path/public.pem’,
’package’ => true
));

8
9
10

$encrypted = $filter->filter(’text_to_be_encoded’);
print $encrypted;

11
12

// For decryption look at the Decrypt filter

Now the returned value contains the encrypted value and the envelope. You don’t need to get them after the compression. But, and this is the negative aspect of this feature, the encrypted value can now only be decrypted by using
ZendFilterEncrypt.

108.9. Encrypt and Decrypt

469
Zend Framework 2 Documentation, Release 2.2.5

Compressing Content

Based on the original value, the encrypted value can be a very large string.
ZendFilterEncrypt allows the usage of compression.

To reduce the value

The compression option can either be set to the name of a compression adapter, or to an array which sets all wished
options for the compression adapter.
1
2
3
4
5
6
7
8

// Use basic compression adapter
$filter1 = new ZendFilterEncrypt(array(
’adapter’
=> ’openssl’,
’private’
=> ’/path/to/mykey/private.pem’,
’public’
=> ’/public/key/path/public.pem’,
’package’
=> true,
’compression’ => ’bz2’
));

9
10
11
12
13
14
15
16
17

// Use basic compression adapter
$filter2 = new ZendFilterEncrypt(array(
’adapter’
=> ’openssl’,
’private’
=> ’/path/to/mykey/private.pem’,
’public’
=> ’/public/key/path/public.pem’,
’package’
=> true,
’compression’ => array(’adapter’ => ’zip’, ’target’ => ’usrtmptmp.zip’)
));

Note: Decryption with same settings
When you want to decrypt a value which is additionally compressed, then you need to set the same compression
settings for decryption as for encryption. Otherwise the decryption will fail.

Decryption with OpenSSL

Decryption with OpenSSL is as simple as encryption. But you need to have all data from the person who encrypted
the content. See the following example:
1
2
3
4
5

// Use openssl and provide a private key
$filter = new ZendFilterDecrypt(array(
’adapter’ => ’openssl’,
’private’ => ’/path/to/mykey/private.pem’
));

6
7
8
9
10
11

// of course you can also give the envelope keys at initiation
$filter->setEnvelopeKey(array(
’/key/from/encoder/first.pem’,
’/key/from/encoder/second.pem’
));

Note: Note that the OpenSSL adapter will not work when you do not provide valid keys.
Optionally it could be necessary to provide the passphrase for decrypting the keys themself by using the
setPassphrase() method.
1
2

// Use openssl and provide a private key
$filter = new ZendFilterDecrypt(array(

470

Chapter 108. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.2.5

3
4
5

’adapter’ => ’openssl’,
’private’ => ’/path/to/mykey/private.pem’
));

6
7
8
9
10
11
12

// of course you can also give the envelope keys at initiation
$filter->setEnvelopeKey(array(
’/key/from/encoder/first.pem’,
’/key/from/encoder/second.pem’
));
$filter->setPassphrase(’mypassphrase’);

At last, decode the content. Our complete example for decrypting the previously encrypted content looks like this.
1
2
3
4
5

// Use openssl and provide a private key
$filter = new ZendFilterDecrypt(array(
’adapter’ => ’openssl’,
’private’ => ’/path/to/mykey/private.pem’
));

6
7
8
9
10
11
12

// of course you can also give the envelope keys at initiation
$filter->setEnvelopeKey(array(
’/key/from/encoder/first.pem’,
’/key/from/encoder/second.pem’
));
$filter->setPassphrase(’mypassphrase’);

13
14
15

$decrypted = $filter->filter(’encoded_text_normally_unreadable’);
print $decrypted;

108.10 HtmlEntities
Returns the string $value, converting characters to their corresponding HTML entity equivalents where they exist.
Supported Options

The following options are supported for ZendFilterHtmlEntities:
• quotestyle: Equivalent to the PHP htmlentities native function parameter quote_style. This allows you to define
what will be done with ‘single’ and “double” quotes. The following constants are accepted: ENT_COMPAT,
ENT_QUOTES ENT_NOQUOTES with the default being ENT_COMPAT.
• charset: Equivalent to the PHP htmlentities native function parameter charset. This defines the character set to
be used in filtering. Unlike the PHP native function the default is ‘UTF-8’. See “http://php.net/htmlentities” for
a list of supported character sets.
Note: This option can also be set via the $options parameter as a Traversable object or array. The option
key will be accepted as either charset or encoding.
• doublequote: Equivalent to the PHP htmlentities native function parameter double_encode. If set to false
existing html entities will not be encoded. The default is to convert everything (true).
Note: This option must be set via the $options parameter or the setDoubleEncode() method.

108.10. HtmlEntities

471
Zend Framework 2 Documentation, Release 2.2.5

Basic Usage

See the following example for the default behavior of this filter.
1

$filter = new ZendFilterHtmlEntities();

2
3

print $filter->filter(’<’);

Quote Style

ZendFilterHtmlEntities allows changing the quote style used. This can be useful when you want to leave
double, single, or both types of quotes un-filtered. See the following example:
1

$filter = new ZendFilterHtmlEntities(array(’quotestyle’ => ENT_QUOTES));

2
3
4

$input = "A ’single’ and " . ’"double"’;
print $filter->filter($input);

The above example returns A &#039;single&#039; and &quot;double&quot;. Notice that ’single’
as well as "double" quotes are filtered.
1

$filter = new ZendFilterHtmlEntities(array(’quotestyle’ => ENT_COMPAT));

2
3
4

$input = "A ’single’ and " . ’"double"’;
print $filter->filter($input);

The above example returns A ’single’ and &quot;double&quot;. Notice that "double" quotes are filtered while ’single’ quotes are not altered.
1

$filter = new ZendFilterHtmlEntities(array(’quotestyle’ => ENT_NOQUOTES));

2
3
4

$input = "A ’single’ and " . ’"double"’;
print $filter->filter($input);

The above example returns A ’single’ and "double". Notice that neither "double" or ’single’ quotes
are altered.
Helper Methods

To change or retrieve the quotestyle after instantiation, the two methods setQuoteStyle() and
getQuoteStyle() may be used respectively. setQuoteStyle() accepts one parameter $quoteStyle. The
following constants are accepted: ENT_COMPAT, ENT_QUOTES, ENT_NOQUOTES
1

$filter = new ZendFilterHtmlEntities();

2
3
4

$filter->setQuoteStyle(ENT_QUOTES);
print $filter->getQuoteStyle(ENT_QUOTES);

To change or retrieve the charset after instantiation, the two methods setCharSet() and getCharSet() may
be used respectively. setCharSet() accepts one parameter $charSet. See “http://php.net/htmlentities” for a list
of supported character sets.
1

$filter = new ZendFilterHtmlEntities();

2
3
4

$filter->setQuoteStyle(ENT_QUOTES);
print $filter->getQuoteStyle(ENT_QUOTES);

472

Chapter 108. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.2.5

To change or retrieve the doublequote option after instantiation, the two methods setDoubleQuote()
and getDoubleQuote() may be used respectively. setDoubleQuote() accepts one boolean parameter
$doubleQuote.
1

$filter = new ZendFilterHtmlEntities();

2
3
4

$filter->setQuoteStyle(ENT_QUOTES);
print $filter->getQuoteStyle(ENT_QUOTES);

108.11 Int
ZendFilterInt allows you to transform a scalar value which contains into an integer.
Supported Options

There are no additional options for ZendFilterInt.
Basic Usage

A basic example of usage is below:
1

$filter = new ZendFilterInt();

2
3

print $filter->filter(’-4 is less than 0’);

This will return ‘-4’.

108.12 Null
This filter will change the given input to be NULL if it meets specific criteria. This is often necessary when you work
with databases and want to have a NULL value instead of a boolean or any other type.
Supported Options

The following options are supported for ZendFilterNull:
• type: The variable type which should be supported.
Default Behavior

Per default this filter works like PHP‘s empty() method; in other words, if empty() returns a boolean TRUE, then
a NULL value will be returned.
1
2
3
4

$filter = new ZendFilterNull();
$value = ’’;
$result = $filter->filter($value);
// returns null instead of the empty string

108.11. Int

473
Zend Framework 2 Documentation, Release 2.2.5

This means that without providing any configuration, ZendFilterNull will accept all input types and return
NULL in the same cases as empty().
Any other value will be returned as is, without any changes.
Changing the Default Behavior

Sometimes it’s not enough to filter based on empty(). Therefor ZendFilterNull allows you to configure
which type will be converted and which not.
The following types can be handled:
• boolean: Converts a boolean FALSE value to NULL.
• integer: Converts an integer 0 value to NULL.
• empty_array: Converts an empty array to NULL.
• float: Converts an float 0.0 value to NULL.
• string: Converts an empty string ‘’ to NULL.
• zero: Converts a string containing the single character zero (‘0’) to NULL.
• all: Converts all above types to NULL. (This is the default behavior.)
There are several ways to select which of the above types are filtered. You can give one or multiple types and add
them, you can give an array, you can use constants, or you can give a textual string. See the following examples:
1
2

// converts false to null
$filter = new ZendFilterNull(ZendFilterNull::BOOLEAN);

3
4
5
6
7

// converts false and 0 to null
$filter = new ZendFilterNull(
ZendFilterNull::BOOLEAN + ZendFilterNull::INTEGER
);

8
9
10
11
12
13

// converts false and 0 to null
$filter = new ZendFilterNull( array(
ZendFilterNull::BOOLEAN,
ZendFilterNull::INTEGER
));

14
15
16
17
18
19

// converts false and 0 to null
$filter = new ZendFilterNull(array(
’boolean’,
’integer’,
));

You can also give a Traversable or an array to set the wished types. To set types afterwards use setType().

108.13 NumberFormat
The NumberFormat filter can be used to return locale-specific number and percentage strings. It extends the
NumberParse filter, which acts as wrapper for the NumberFormatter class within the Internationalization extension (Intl).

474

Chapter 108. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.2.5

Supported Options for NumberFormat Filter

The following options are supported for NumberFormat:
NumberFormat([ string $locale [, int $style [, int $type ]]])
• $locale: (Optional) Locale in which the number would be formatted (locale name, e.g. en_US). If unset, it
will use the default locale (Locale::getDefault())
Methods for getting/setting the locale are also available: getLocale() and setLocale()
• $style: (Optional) Style of the formatting, one of the format style constants.
NumberFormatter::DEFAULT_STYLE as the default style.

If unset, it will use

Methods for getting/setting the format style are also available: getStyle() and setStyle()
• $type: (Optional) The formatting type to use. If unset, it will use NumberFormatter::TYPE_DOUBLE
as the default type.
Methods for getting/setting the format type are also available: getType() and setType()
NumberFormat Filter Usage

1
2
3

$filter = new ZendI18nFilterNumberFormat("de_DE");
echo $filter->filter(1234567.8912346);
// Returns "1.234.567,891"

4
5
6
7

$filter = new ZendI18nFilterNumberFormat("en_US", NumberFormatter::PERCENT);
echo $filter->filter(0.80);
// Returns "80%"

8
9
10
11

$filter = new ZendI18nFilterNumberFormat("fr_FR", NumberFormatter::SCIENTIFIC);
echo $filter->filter(0.00123456789);
// Returns "1,23456789E-3"

108.14 PregReplace
ZendFilterPregReplace performs a search using regular expressions and replaces all found elements.
Supported Options

The following options are supported for ZendFilterPregReplace:
• pattern: The pattern which will be searched for.
• replacement: The string which is used as replacement for the matches.
Basic Usage

To use this filter properly you must give two options:
The option pattern has to be given to set the pattern which will be searched for. It can be a string for a single
pattern, or an array of strings for multiple pattern.

108.14. PregReplace

475
Zend Framework 2 Documentation, Release 2.2.5

To set the pattern which will be used as replacement the option replacement has to be used. It can be a string for
a single pattern, or an array of strings for multiple pattern.
1
2
3
4
5

$filter = new ZendFilterPregReplace(array(
’pattern’
=> ’/bob/’,
’replacement’ => ’john’,
));
$input = ’Hi bob!’;

6
7
8

$filter->filter($input);
// returns ’Hi john!’

You can use getPattern() and setPattern() to set the matching pattern afterwards. To set the replacement
pattern you can use getReplacement() and setReplacement().
1
2
3
4

$filter = new ZendFilterPregReplace();
$filter->setMatchPattern(array(’bob’, ’Hi’))
->setReplacement(array(’john’, ’Bye’));
$input = ’Hi bob!’;

5
6
7

$filter->filter($input);
// returns ’Bye john!’

For a more complex usage take a look into PHP‘s PCRE Pattern Chapter.

108.15 RealPath
This filter will resolve given links and pathnames and returns canonicalized absolute pathnames.
Supported Options

The following options are supported for ZendFilterRealPath:
• exists: This option defaults to TRUE which checks if the given path really exists.
Basic Usage

For any given link of pathname its absolute path will be returned. References to ‘/./‘, ‘/../‘ and extra ‘/‘ characters
in the input path will be stripped. The resulting path will not have any symbolic link, ‘/./‘ or ‘/../‘ character.
ZendFilterRealPath will return FALSE on failure, e.g. if the file does not exist. On BSD systems
ZendFilterRealPath doesn’t fail if only the last path component doesn’t exist, while other systems will
return FALSE.
1
2
3

$filter = new ZendFilterRealPath();
$path
= ’/www/var/path/../../mypath’;
$filtered = $filter->filter($path);

4
5

// returns ’/www/mypath’

Non-Existing Paths

Sometimes it is useful to get also paths when they don’t exist, f.e. when you want to get the real path for a path which
you want to create. You can then either give a FALSE at initiation, or use setExists() to set it.
476

Chapter 108. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.2.5

1
2
3

$filter = new ZendFilterRealPath(false);
$path
= ’/www/var/path/../../non/existing/path’;
$filtered = $filter->filter($path);

4
5
6

// returns ’/www/non/existing/path’
// even when file_exists or realpath would return false

108.16 StringToLower
This filter converts any input to be lowercased.
Supported Options

The following options are supported for ZendFilterStringToLower:
• encoding: This option can be used to set an encoding which has to be used.
Basic Usage

This is a basic example:
1

$filter = new ZendFilterStringToLower();

2
3
4

print $filter->filter(’SAMPLE’);
// returns "sample"

Different Encoded Strings

Per default it will only handle characters from the actual locale of your server. Characters from other charsets would
be ignored. Still, it’s possible to also lowercase them when the mbstring extension is available in your environment.
Simply set the wished encoding when initiating the StringToLower filter. Or use the setEncoding() method
to change the encoding afterwards.
1
2

// using UTF-8
$filter = new ZendFilterStringToLower(’UTF-8’);

3
4
5

// or give an array which can be useful when using a configuration
$filter = new ZendFilterStringToLower(array(’encoding’ => ’UTF-8’));

6
7
8

// or do this afterwards
$filter->setEncoding(’ISO-8859-1’);

Note: Setting wrong encodings
Be aware that you will get an exception when you want to set an encoding and the mbstring extension is not available
in your environment.
Also when you are trying to set an encoding which is not supported by your mbstring extension you will get an
exception.

108.16. StringToLower

477
Zend Framework 2 Documentation, Release 2.2.5

108.17 StringToUpper
This filter converts any input to be uppercased.
Supported Options

The following options are supported for ZendFilterStringToUpper:
• encoding: This option can be used to set an encoding which has to be used.
Basic Usage

This is a basic example for using the StringToUpper filter:
1

$filter = new ZendFilterStringToUpper();

2
3
4

print $filter->filter(’Sample’);
// returns "SAMPLE"

Different Encoded Strings

Like the StringToLower filter, this filter handles only characters from the actual locale of your server. Using
different character sets works the same as with StringToLower.
1

$filter = new ZendFilterStringToUpper(array(’encoding’ => ’UTF-8’));

2
3
4

// or do this afterwards
$filter->setEncoding(’ISO-8859-1’);

108.18 StringTrim
This filter modifies a given string such that certain characters are removed from the beginning and end.
Supported Options

The following options are supported for ZendFilterStringTrim:
• charlist: List of characters to remove from the beginning and end of the string. If this is not set or is null, the
default behavior will be invoked, which is to remove only whitespace from the beginning and end of the string.
Basic Usage

A basic example of usage is below:
1

$filter = new ZendFilterStringTrim();

2
3

print $filter->filter(’ This is (my) content: ’);

The above example returns ‘This is (my) content:’. Notice that the whitespace characters have been removed.

478

Chapter 108. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.2.5

Default Behavior

1
2

$filter = new ZendFilterStringTrim(’:’);
// or new ZendFilterStringTrim(array(’charlist’ => ’:’));

3
4

print $filter->filter(’ This is (my) content:’);

The above example returns ‘This is (my) content’. Notice that the whitespace characters and colon are removed. You
can also provide a Traversable or an array with a ‘charlist’ key. To set the desired character list after instantiation, use
the setCharList() method. The getCharList() return the values set for charlist.

108.19 StripNewLines
This filter modifies a given string and removes all new line characters within that string.
Supported Options

There are no additional options for ZendFilterStripNewLines:
Basic Usage

A basic example of usage is below:
1

$filter = new ZendFilterStripNewLines();

2
3

print $filter->filter(’ This is (my)‘‘nr‘‘content: ’);

The above example returns ‘This is (my) content:’. Notice that all newline characters have been removed.

108.20 StripTags
This filter can strip XML and HTML tags from given content.
Warning: ZendFilterStripTags is potentially unsecure
Be warned that ZendFilterStripTags should only be used to strip all available tags.
Using ZendFilterStripTags to make your site secure by stripping some unwanted tags will lead to unsecure and
dangerous code.
ZendFilterStripTags must not be used to prevent XSS attacks. This filter is no replacement for using Tidy or
HtmlPurifier.

Supported Options

The following options are supported for ZendFilterStripTags:
• allowAttribs: This option sets the attributes which are accepted. All other attributes are
stripped from the given content.
• allowTags: This option sets the tags which are accepted. All other tags will be stripped

108.19. StripNewLines

479
Zend Framework 2 Documentation, Release 2.2.5

from the given content.
Basic Usage

See the following example for the default behaviour of this filter:
1

$filter = new ZendFilterStripTags();

2
3

print $filter->filter(’<B>My content</B>’);

As result you will get the stripped content ‘My content’.
When the content contains broken or partial tags then the complete following content will be erased. See the following
example:
1

$filter = new ZendFilterStripTags();

2
3

print $filter->filter(’This contains <a href="http://example.com">no ending tag’);

The above will return ‘This contains’ with the rest being stripped.
Allowing Defined Tags

ZendFilterStripTags allows stripping of all but defined tags. This can be used for example to strip all tags
but links from a text.
1

$filter = new ZendFilterStripTags(array(’allowTags’ => ’a’));

2
3
4

$input = "A text with <br/> a <a href=’link.com’>link</a>";
print $filter->filter($input);

The above will return ‘A text with a <a href=’link.com’>link</a>’ as result. It strips all tags but the link. By providing
an array you can set multiple tags at once.
Warning: Do not use this feature to get a probably secure content. This component does not replace the use of a
proper configured html filter.

Allowing Defined Attributes

It is also possible to strip all but allowed attributes from a tag.
1

$filter = new ZendFilterStripTags(array(’allowTags’ => ’img’, ’allowAttribs’ => ’src’));

2
3
4

$input = "A text with <br/> a <img src=’picture.com’ width=’100’>picture</img>";
print $filter->filter($input);

The above will return ‘A text with a <img src=’picture.com’>picture</img>’ as result. It strips all tags but img.
Additionally from the img tag all attributes but src will be stripped. By providing an array you can set multiple
attributes at once.

480

Chapter 108. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.2.5

108.21 UriNormalize
This filter can set a scheme on an URI, if a scheme is not present. If a scheme is present, that scheme will not be
affected, even if a different scheme is enforced.
Supported Options

The following options are supported for ZendFilterUriNormalize:
• defaultScheme: This option can be used to set the default scheme to use when parsing scheme-less URIs.
• enforcedScheme: Set a URI scheme to enforce on schemeless URIs.
Basic Usage

See the following example for the default behaviour of this filter:
1
2
3

$filter = new ZendFilterUriNormalize(array(
’enforcedScheme’ => ’https’
));

4
5

echo $filter->filter(’www.example.com’);

As the result the string https://www.example.com will be output.

108.21. UriNormalize

481
Zend Framework 2 Documentation, Release 2.2.5

482

Chapter 108. Standard Filter Classes
CHAPTER 109

Word Filters

In addition to the standard set of filters, there are several classes specific to filtering word strings.

109.1 CamelCaseToDash
This filter modifies a given string such that ‘CamelCaseWords’ are converted to ‘Camel-Case-Words’.
Supported Options

There are no additional options for ZendFilterWordCamelCaseToDash:
Basic Usage

A basic example of usage is below:
1

$filter = new ZendFilterWordCamelCaseToDash();

2
3

print $filter->filter(’ThisIsMyContent’);

The above example returns ‘This-Is-My-Content’.

109.2 CamelCaseToSeparator
This filter modifies a given string such that ‘CamelCaseWords’ are converted to ‘Camel Case Words’.
Supported Options

The following options are supported for ZendFilterWordCamelCaseToSeparator:
• separator: A separator char. If this is not set the separator will be a space character.

483
Zend Framework 2 Documentation, Release 2.2.5

Basic Usage

A basic example of usage is below:
1
2

$filter = new ZendFilterWordCamelCaseToSeparator(’:’);
// or new ZendFilterWordCamelCaseToSeparator(array(’separator’ => ’:’));

3
4

print $filter->filter(’ThisIsMyContent’);

The above example returns ‘This:Is:My:Content’.
Default Behavior

1

$filter = new ZendFilterWordCamelCaseToSeparator();

2
3

print $filter->filter(’ThisIsMyContent’);

The above example returns ‘This Is My Content’.

109.3 CamelCaseToUnderscore
This filter modifies a given string such that ‘CamelCaseWords’ are converted to ‘Camel_Case_Words’.
Supported Options

There are no additional options for ZendFilterWordCamelCaseToUnderscore:
Basic usage

A basic example of usage is below:
1

$filter = new ZendFilterWordCamelCaseToUnderscore();

2
3

print $filter->filter(’ThisIsMyContent’);

The above example returns ‘This_Is_My_Content’.

109.4 DashToCamelCase
This filter modifies a given string such that ‘words-with-dashes’ are converted to ‘WordsWithDashes’.
Supported Options

There are no additional options for ZendFilterWordDashToCamelCase:

484

Chapter 109. Word Filters
Zend Framework 2 Documentation, Release 2.2.5

Basic Usage

A basic example of usage is below:
1

$filter = new ZendFilterWordDashToCamelCase();

2
3

print $filter->filter(’this-is-my-content’);

The above example returns ‘ThisIsMyContent’.

109.5 DashToSeparator
This filter modifies a given string such that ‘words-with-dashes’ are converted to ‘words with dashes’.
Supported Options

The following options are supported for ZendFilterWordDashToSeparator:
• separator: A separator char. If this is not set the separator will be a space character.
Basic Usage

A basic example of usage is below:
1
2

$filter = new ZendFilterWordDashToSeparator(’+’);
// or new ZendFilterWordCamelCaseToSeparator(array(’separator’ => ’+’));

3
4

print $filter->filter(’this-is-my-content’);

The above example returns ‘this+is+my+content’.
Default Behavior

1

$filter = new ZendFilterWordDashToSeparator();

2
3

print $filter->filter(’this-is-my-content’);

The above example returns ‘this is my content’.

109.6 DashToUnderscore
This filter modifies a given string such that ‘words-with-dashes’ are converted to ‘words_with_dashes’.
Supported Options

There are no additional options for ZendFilterWordDashToUnderscore:

109.5. DashToSeparator

485
Zend Framework 2 Documentation, Release 2.2.5

Basic Usage

A basic example of usage is below:
1

$filter = new ZendFilterWordDashToUnderscore();

2
3

print $filter->filter(’this-is-my-content’);

The above example returns ‘this_is_my_content’.

109.7 SeparatorToCamelCase
This filter modifies a given string such that ‘words with separators’ are converted to ‘WordsWithSeparators’.
Supported Options

The following options are supported for ZendFilterWordSeparatorToCamelCase:
• separator: A separator char. If this is not set the separator will be a space character.
Basic Usage

A basic example of usage is below:
1
2

$filter = new ZendFilterWordSeparatorToCamelCase(’:’);
// or new ZendFilterWordSeparatorToCamelCase(array(’separator’ => ’:’));

3
4

print $filter->filter(’this:is:my:content’);

The above example returns ‘ThisIsMyContent’.
Default Behavior

1

$filter = new ZendFilterWordSeparatorToCamelCase();

2
3

print $filter->filter(’this is my content’);

The above example returns ‘ThisIsMyContent’.

109.8 SeparatorToDash
This filter modifies a given string such that ‘words with separators’ are converted to ‘words-with-separators’.
Supported Options

The following options are supported for ZendFilterWordSeparatorToDash:
• separator: A separator char. If this is not set the separator will be a space character.

486

Chapter 109. Word Filters
Zend Framework 2 Documentation, Release 2.2.5

Basic Usage

A basic example of usage is below:
1
2

$filter = new ZendFilterWordSeparatorToDash(’:’);
// or new ZendFilterWordSeparatorToDash(array(’separator’ => ’:’));

3
4

print $filter->filter(’this:is:my:content’);

The above example returns ‘this-is-my-content’.
Default Behavior

1

$filter = new ZendFilterWordSeparatorToDash();

2
3

print $filter->filter(’this is my content’);

The above example returns ‘this-is-my-content’.

109.9 SeparatorToSeparator
This filter modifies a given string such that ‘words with separators’ are converted to ‘words-with-separators’.
Supported Options

The following options are supported for ZendFilterWordSeparatorToSeparator:
• searchSeparator: The search separator char. If this is not set the separator will be a space character.
• replaceSeparator: The replace separator char. If this is not set the separator will be a dash.
Basic Usage

A basic example of usage is below:
1

$filter = new ZendFilterWordSeparatorToSeparator(’:’, ’+’);

2
3

print $filter->filter(’this:is:my:content’);

The above example returns ‘this+is+my+content’.
Default Behaviour

1

$filter = new ZendFilterWordSeparatorToSeparator();

2
3

print $filter->filter(’this is my content’);

The above example returns ‘this-is-my-content’.

109.9. SeparatorToSeparator

487
Zend Framework 2 Documentation, Release 2.2.5

109.10 UnderscoreToCamelCase
This filter modifies a given string such that ‘words_with_underscores’ are converted to ‘WordsWithUnderscores’.
Supported Options

There are no additional options for ZendFilterWordUnderscoreToCamelCase:
Basic Usage

A basic example of usage is below:
1

$filter = new ZendFilterWordUnderscoreToCamelCase();

2
3

print $filter->filter(’this_is_my_content’);

The above example returns ‘ThisIsMyContent’.

109.11 UnderscoreToSeparator
This filter modifies a given string such that ‘words_with_underscores’ are converted to ‘words with underscores’.
Supported Options

The following options are supported for ZendFilterWordUnderscoreToSeparator:
• separator: A separator char. If this is not set the separator will be a space character.
Basic usage

A basic example of usage is below:
1
2

$filter = new ZendFilterWordUnderscoreToSeparator(’+’);
// or new ZendFilterWordCamelCaseToSeparator(array(’separator’ => ’+’));

3
4

print $filter->filter(’this_is_my_content’);

The above example returns ‘this+is+my+content’.
Default Behavior

1

$filter = new ZendFilterWordUnderscoreToSeparator();

2
3

print $filter->filter(’this_is_my_content’);

The above example returns ‘this is my content’.

488

Chapter 109. Word Filters
Zend Framework 2 Documentation, Release 2.2.5

109.12 UnderscoreToDash
This filter modifies a given string such that ‘words_with_underscores’ are converted to ‘words-with-underscores’.
Supported Options

There are no additional options for ZendFilterWordUnderscoreToDash:
Basic usage

A basic example of usage is below:
1

$filter = new ZendFilterWordUnderscoreToDash();

2
3

print $filter->filter(’this_is_my_content’);

The above example returns ‘this-is-my-content’.

109.12. UnderscoreToDash

489
Zend Framework 2 Documentation, Release 2.2.5

490

Chapter 109. Word Filters
CHAPTER 110

File Filter Classes

Zend Framework comes with a set of classes for filtering file contents as well as performing other actions, such as file
renaming.
Note: All of the File Filter Classes’ filter() methods support both a file path string or a $_FILES array as the
supplied argument. When a $_FILES array is passed in, the tmp_name is used for the file path.

110.1 Decrypt
TODO

110.2 Encrypt
TODO

110.3 Lowercase
TODO

110.4 Rename
ZendFilterFileRename can be used to rename a file and/or move a file to a new path.
Supported Options

The following set of options are supported:
• target (string) default:

"*" Target filename or directory, the new name of the source file.

491
Zend Framework 2 Documentation, Release 2.2.5

• source (string) default:

"*" Source filename or directory which will be renamed.

Used to match the filtered file with an options set.
• overwrite (boolean) default:

false Shall existing files be overwritten?

If
the
file
is
unable
to
be
moved
into
the
ZendFilterExceptionRuntimeException will be thrown.

target

path,

a

• randomize (boolean) default: false Shall target files have a random postfix attached? The random postfix will be a uniqid(’_’) after the file name and before the extension.
For example, "file.txt" will be randomized to "file_4b3403665fea6.txt"
An array of option sets is also supported, where a single Rename filter instance can filter several files using different
options. The options used for the filtered file will be matched from the source option in the options set.
Usage Examples

Move all filtered files to a different directory:
1
2
3
4

// ’target’ option is assumed if param is a string
$filter = ZendFilterFileRename("/tmp/");
echo $filter->filter("./myfile.txt");
// File has been moved to "/tmp/myfile.txt"

Rename all filtered files to a new name:
1
2
3

$filter = ZendFilterFileRename("/tmp/newfile.txt");
echo $filter->filter("./myfile.txt");
// File has been renamed to "/tmp/newfile.txt"

Move to a new path and randomize file names:
1
2
3
4
5
6

$filter = ZendFilterFileRename(array(
"target"
=> "/tmp/newfile.txt",
"randomize" => true,
));
echo $filter->filter("./myfile.txt");
// File has been renamed to "/tmp/newfile_4b3403665fea6.txt"

Configure different options for several possible source files:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

$filter = ZendFilterFileRename(array(
array(
"source"
=> "fileA.txt"
"target"
=> "/dest1/newfileA.txt",
"overwrite" => true,
),
array(
"source"
=> "fileB.txt"
"target"
=> "/dest2/newfileB.txt",
"randomize" => true,
),
));
echo $filter->filter("fileA.txt");
// File has been renamed to "/dest1/newfileA.txt"
echo $filter->filter("fileB.txt");
// File has been renamed to "/dest2/newfileB_4b3403665fea6.txt"

492

Chapter 110. File Filter Classes
Zend Framework 2 Documentation, Release 2.2.5

Public Methods

The specific public methods for the Rename filter, besides the common filter() method, are as follows:
getFile()
Returns the files to rename and their new name and location
Return type array
setFile(string|array $options)
Sets the file options for renaming. Removes any previously set file options.
Parameters $options – See Supported Options section for more information.
addFile(string|array $options)
Adds file options for renaming to the current list of file options.
Parameters $options – See Supported Options section for more information.

110.5 RenameUpload
ZendFilterFileRenameUpload can be used to rename or move an uploaded file to a new path.
Supported Options

The following set of options are supported:
• target (string) default:

"*" Target directory or full filename path.

• overwrite (boolean) default:

false Shall existing files be overwritten?

If
the
file
is
unable
to
be
moved
into
the
ZendFilterExceptionRuntimeException will be thrown.

target

path,

a

• randomize (boolean) default: false Shall target files have a random postfix attached? The random postfix will be a uniqid(’_’) after the file name and before the extension.
For example, "file.txt" will be randomized to "file_4b3403665fea6.txt"
• use_upload_name (boolean) default: false When true, this filter will use the $_FILES[’name’]
as the target filename. Otherwise, the default target rules and the $_FILES[’tmp_name’] will be
used.
• use_upload_extension (boolean) default:
original extension if not specified.

false When true, the uploaded file will maintains its

For example, if the uploaded file is "file.txt" and the target is something like "mynewfile", the
upload will be renamed to "mynewfile.txt".
Warning: Be very careful when using the use_upload_name option. For instance, extremely bad things could
happen if you were to allow uploaded .php files (or other CGI files) to be moved into the DocumentRoot.
It is generally a better idea to supply an internal filename to avoid security risks.
RenameUpload does not support an array of options like the‘‘Rename‘‘ filter. When filtering HTML5 file uploads
with the multiple attribute set, all files will be filtered with the same option settings.

110.5. RenameUpload

493
Zend Framework 2 Documentation, Release 2.2.5

Usage Examples

Move all filtered files to a different directory:
1

use ZendHttpPhpEnvironmentRequest;

2
3
4
5
6

$request = new Request();
$files
= $request->getFiles();
// i.e. $files[’my-upload’][’tmp_name’] === ’/tmp/php5Wx0aJ’
// i.e. $files[’my-upload’][’name’] === ’myfile.txt’

7
8
9
10
11

// ’target’ option is assumed if param is a string
$filter = ZendFilterFileRenameUpload("./data/uploads/");
echo $filter->filter($files[’my-upload’]);
// File has been moved to "./data/uploads/php5Wx0aJ"

12
13
14
15
16

// ... or retain the uploaded file name
$filter->setUseUploadName(true);
echo $filter->filter($files[’my-upload’]);
// File has been moved to "./data/uploads/myfile.txt"

Rename all filtered files to a new name:
1

use ZendHttpPhpEnvironmentRequest;

2
3
4
5

$request = new Request();
$files
= $request->getFiles();
// i.e. $files[’my-upload’][’tmp_name’] === ’/tmp/php5Wx0aJ’

6
7
8
9

$filter = ZendFilterFileRename("./data/uploads/newfile.txt");
echo $filter->filter($files[’my-upload’]);
// File has been renamed to "./data/uploads/newfile.txt"

Move to a new path and randomize file names:
1

use ZendHttpPhpEnvironmentRequest;

2
3
4
5

$request = new Request();
$files
= $request->getFiles();
// i.e. $files[’my-upload’][’tmp_name’] === ’/tmp/php5Wx0aJ’

6
7
8
9
10
11
12

$filter = ZendFilterFileRename(array(
"target"
=> "./data/uploads/newfile.txt",
"randomize" => true,
));
echo $filter->filter($files[’my-upload’]);
// File has been renamed to "./data/uploads/newfile_4b3403665fea6.txt"

110.6 Uppercase
TODO

494

Chapter 110. File Filter Classes
CHAPTER 111

Filter Chains

Often multiple filters should be applied to some value in a particular order. For example, a login form accepts a
username that should be only lowercase, alphabetic characters. ZendFilterFilterChain provides a simple
method by which filters may be chained together. The following code illustrates how to chain together two filters for
the submitted username:
1
2
3
4

// Create a filter chain and add filters to the chain
$filterChain = new ZendFilterFilterChain();
$filterChain->attach(new ZendFilterAlpha())
->attach(new ZendFilterStringToLower());

5
6
7

// Filter the username
$username = $filterChain->filter($_POST[’username’]);

Filters are run in the order they were added to ZendFilterFilterChain. In the above example, the username
is first removed of any non-alphabetic characters, and then any uppercase characters are converted to lowercase.
Any object that implements ZendFilterFilterInterface may be used in a filter chain.

111.1 Setting Filter Chain Order
For each filter added to the FilterChain you can set a priority to define the chain order. The default value is 1000.
In the following example, any uppercase characters are converted to lowercase before any non-alphabetic characters
are removed.
1
2
3
4

// Create a filter chain and add filters to the chain
$filterChain = new ZendFilterFilterChain();
$filterChain->attach(new ZendFilterAlpha())
->attach(new ZendFilterStringToLower(), 500);

111.2 Using the Plugin Manager
To every FilterChain object an instance of the FilterPluginManager is attached. Every filter that is
used in a FilterChain must be know by this FilterPluginManager. To add a filter that is known by the
FilterPluginManager you can use the attachByName() method. The first parameter is the name of the

495
Zend Framework 2 Documentation, Release 2.2.5

filter within the FilterPluginManager. The second parameter takes any options for creating the filter instance.
The third parameter is the priority.
1
2
3
4

// Create a filter chain and add filters to the chain
$filterChain = new ZendFilterFilterChain();
$filterChain->attachByName(’alpha’)
->attachByName(’stringtolower’, array(’encoding’ => ’utf-8’), 500);

The following example shows how to add a custom filter to the FilterPluginManager and the FilterChain.
1
2
3
4
5
6

$filterChain = new ZendFilterFilterChain();
$filterChain->getPluginManager()->setInvokableClass(
’myNewFilter’, ’MyCustomFilterMyNewFilter’
);
$filterChain->attachByName(’alpha’)
->attachByName(’myNewFilter’);

You can also add your own FilterPluginManager implementation.
1
2
3
4

$filterChain = new ZendFilterFilterChain();
$filterChain->setPluginManager(new MyFilterPluginManager());
$filterChain->attach(new ZendFilterAlpha())
->attach(new MyCustomFilterMyNewFilter());

496

Chapter 111. Filter Chains
CHAPTER 112

ZendFilterInflector

ZendFilterInflector is a general purpose tool for rules-based inflection of strings to a given target.
As an example, you may find you need to transform MixedCase or camelCasedWords into a path; for readability, OS
policies, or other reasons, you also need to lower case this, and you want to separate the words using a dash (‘-‘). An
inflector can do this for you.
ZendFilterInflector implements ZendFilterFilterInterface; you perform inflection by calling filter() on the object instance.
Transforming MixedCase and camelCaseText to another format

1
2
3
4
5

$inflector = new ZendFilterInflector(’pages/:page.:suffix’);
$inflector->setRules(array(
’:page’ => array(’WordCamelCaseToDash’, ’StringToLower’),
’suffix’ => ’html’,
));

6
7
8
9

$string
= ’camelCasedWords’;
$filtered = $inflector->filter(array(’page’ => $string));
// pages/camel-cased-words.html

10
11
12
13

$string
= ’this_is_not_camel_cased’;
$filtered = $inflector->filter(array(’page’ => $string));
// pages/this_is_not_camel_cased.html

112.1 Operation
An inflector requires a target and one or more rules. A target is basically a string that defines placeholders for
variables you wish to substitute. These are specified by prefixing with a ‘:’: :script.
When calling filter(), you then pass in an array of key and value pairs corresponding to the variables in the target.
Each variable in the target can have zero or more rules associated with them. Rules may be either static or refer to a
ZendFilter class. Static rules will replace with the text provided. Otherwise, a class matching the rule provided
will be used to inflect the text. Classes are typically specified using a short name indicating the filter name stripped of
any common prefix.

497
Zend Framework 2 Documentation, Release 2.2.5

As an example, you can use any ZendFilter concrete implementations; however, instead of referring to
them as ‘ZendFilterAlpha‘ or ‘ZendFilterStringToLower‘, you’d specify only ‘Alpha‘ or
‘StringToLower‘.

112.2 Using Custom Filters
ZendFilterInflector uses ZendFilterFilterPluginManager to manage loading filters to use
with inflection. By default, filters registered with ZendFilterFilterPluginManager are available. To
access filters with that prefix but which occur deeper in the hierarchy, such as the various Word filters, simply strip off
the ZendFilter prefix:
1
2

// use ZendFilterWordCamelCaseToDash as a rule
$inflector->addRules(array(’script’ => ’WordCamelCaseToDash’));

To use custom filters, you have two choices: reference them by fully qualified class name (e.g.,
MyCustomFilterMungify), or manipulate the composed FilterPluginManager instance.
1
2

$filters = $inflector->getPluginManager();
$filters->addInvokableClass(’mungify’, ’MyCustomFilterMungify’);

112.3 Setting the Inflector Target
The inflector target is a string with some placeholders for variables. Placeholders take the form of an identifier, a colon
(‘:’) by default, followed by a variable name: ‘:script’, ‘:path’, etc. The filter() method looks for the identifier
followed by the variable name being replaced.
You can change the identifier using the setTargetReplacementIdentifier() method, or passing it as the
third argument to the constructor:
1
2

// Via constructor:
$inflector = new ZendFilterInflector(’#foo/#bar.#sfx’, null, ’#’);

3
4
5

// Via accessor:
$inflector->setTargetReplacementIdentifier(’#’);

Typically, you will set the target via the constructor. However, you may want to re-set the target later. setTarget()
can be used for this purpose:
1

$inflector->setTarget(’layouts/:script.phtml’);

Additionally, you may wish to have a class member for your class that you can use to keep the inflector target updated
– without needing to directly update the target each time (thus saving on method calls). setTargetReference()
allows you to do this:
1
2
3
4
5
6

class Foo
{
/**
* @var string Inflector target
*/
protected $_target = ’foo/:bar/:baz.:suffix’;

7

/**
* Constructor
* @return void
*/

8
9
10
11

498

Chapter 112. ZendFilterInflector
Zend Framework 2 Documentation, Release 2.2.5

public function __construct()
{
$this->_inflector = new ZendFilterInflector();
$this->_inflector->setTargetReference($this->_target);
}

12
13
14
15
16
17

/**
* Set target; updates target in inflector
*
* @param string $target
* @return Foo
*/
public function setTarget($target)
{
$this->_target = $target;
return $this;
}

18
19
20
21
22
23
24
25
26
27
28
29

}

112.4 Inflection Rules
As mentioned in the introduction, there are two types of rules: static and filter-based.
Note: It is important to note that regardless of the method in which you add rules to the inflector, either one-byone, or all-at-once; the order is very important. More specific names, or names that might contain other rule names,
must be added before least specific names. For example, assuming the two rule names ‘moduleDir’ and ‘module’,
the ‘moduleDir’ rule should appear before module since ‘module’ is contained within ‘moduleDir’. If ‘module’ were
added before ‘moduleDir’, ‘module’ will match part of ‘moduleDir’ and process it leaving ‘Dir’ inside of the target
uninflected.

112.4.1 Static Rules
Static rules do simple string substitution; use them when you have a segment in the target that will typically be static,
but which you want to allow the developer to modify. Use the setStaticRule() method to set or modify the rule:
1
2

$inflector = new ZendFilterInflector(’:script.:suffix’);
$inflector->setStaticRule(’suffix’, ’phtml’);

3
4
5

// change it later:
$inflector->setStaticRule(’suffix’, ’php’);

Much like the target itself, you can also bind a static rule to a reference, allowing you to update a single variable
instead of require a method call; this is often useful when your class uses an inflector internally, and you don’t want
your users to need to fetch the inflector in order to update it. The setStaticRuleReference() method is used
to accomplish this:
1
2
3
4
5
6

class Foo
{
/**
* @var string Suffix
*/
protected $_suffix = ’phtml’;

7

112.4. Inflection Rules

499
Zend Framework 2 Documentation, Release 2.2.5

/**
* Constructor
* @return void
*/
public function __construct()
{
$this->_inflector = new ZendFilterInflector(’:script.:suffix’);
$this->_inflector->setStaticRuleReference(’suffix’, $this->_suffix);
}

8
9
10
11
12
13
14
15
16
17

/**
* Set suffix; updates suffix static rule in inflector
*
* @param string $suffix
* @return Foo
*/
public function setSuffix($suffix)
{
$this->_suffix = $suffix;
return $this;
}

18
19
20
21
22
23
24
25
26
27
28
29

}

112.4.2 Filter Inflector Rules
ZendFilter filters may be used as inflector rules as well. Just like static rules, these are bound to a target variable;
unlike static rules, you may define multiple filters to use when inflecting. These filters are processed in order, so be
careful to register them in an order that makes sense for the data you receive.
Rules may be added using setFilterRule() (which overwrites any previous rules for that variable) or
addFilterRule() (which appends the new rule to any existing rule for that variable). Filters are specified in
one of the following ways:
• String. The string may be a filter class name, or a class name segment minus any prefix set in the inflector’s
plugin loader (by default, minus the ‘ZendFilter‘ prefix).
• Filter object. Any object instance implementing ZendFilterFilterInterface may be passed as a
filter.
• Array. An array of one or more strings or filter objects as defined above.
1

$inflector = new ZendFilterInflector(’:script.:suffix’);

2
3
4

// Set rule to use ZendFilterWordCamelCaseToDash filter
$inflector->setFilterRule(’script’, ’WordCamelCaseToDash’);

5
6
7

// Add rule to lowercase string
$inflector->addFilterRule(’script’, new ZendFilterStringToLower());

8
9
10
11
12
13

// Set rules en-masse
$inflector->setFilterRule(’script’, array(
’WordCamelCaseToDash’,
new ZendFilterStringToLower()
));

500

Chapter 112. ZendFilterInflector
Zend Framework 2 Documentation, Release 2.2.5

112.4.3 Setting Many Rules At Once
Typically, it’s easier to set many rules at once than to configure a single variable and its inflection rules at a time.
ZendFilterInflector‘s addRules() and setRules() method allow this.
Each method takes an array of variable and rule pairs, where the rule may be whatever the type of rule accepts (string,
filter object, or array). Variable names accept a special notation to allow setting static rules and filter rules, according
to the following notation:
• ‘:’ prefix: filter rules.
• No prefix: static rule.
Setting Multiple Rules at Once

1
2
3
4
5

// Could also use setRules() with this notation:
$inflector->addRules(array(
// filter rules:
’:controller’ => array(’CamelCaseToUnderscore’,’StringToLower’),
’:action’
=> array(’CamelCaseToUnderscore’,’StringToLower’),

6

// Static rule:
’suffix’
=> ’phtml’

7
8
9

));

112.5 Utility Methods
ZendFilterInflector has a number of utility methods for retrieving and setting the plugin loader, manipulating and retrieving rules, and controlling if and when exceptions are thrown.
• setPluginManager()
can
be
used
when
you
have
configured
your
own
ZendFilterFilterPluginManager instance and wish to use it with ZendFilterInflector;
getPluginManager() retrieves the currently set one.
• setThrowTargetExceptionsOn() can be used to control whether or not filter() throws an exception when a given replacement identifier passed to it is not found in the target. By default, no exceptions are
thrown. isThrowTargetExceptionsOn() will tell you what the current value is.
• getRules($spec = null) can be used to retrieve all registered rules for all variables, or just the rules for
a single variable.
• getRule($spec, $index) fetches a single rule for a given variable; this can be useful for fetching a
specific filter rule for a variable that has a filter chain. $index must be passed.
• clearRules() will clear all currently registered rules.

112.6 Using a Traversable or an array with ZendFilterInflector
You can use a Traversable or an array to set rules and other object state in your inflectors, either by passing a
Traversable object or an array to the constructor or setOptions(). The following settings may be specified:
• target specifies the inflection target.

112.5. Utility Methods

501
Zend Framework 2 Documentation, Release 2.2.5

• pluginManager specifies the ZendFilterFilterPluginManager instance or extension to
use for obtaining plugins; alternately, you may specify a class name of a class that extends the
FilterPluginManager.
• throwTargetExceptionsOn should be a boolean indicating whether or not to throw exceptions when a
replacement identifier is still present after inflection.
• targetReplacementIdentifier specifies the character to use when identifying replacement variables
in the target string.
• rules specifies an array of inflection rules; it should consist of keys that specify either values or arrays of
values, consistent with addRules().
Using a Traversable or an array with ZendFilterInflector

1
2
3

// With the constructor:
$options; // implements Traversable
$inflector = new ZendFilterInflector($options);

4
5
6
7

// Or with setOptions():
$inflector = new ZendFilterInflector();
$inflector->setOptions($options);

502

Chapter 112. ZendFilterInflector
CHAPTER 113

Writing Filters

ZendFilter supplies a set of commonly needed filters, but developers will often need to write custom filters for their particular use cases. The task of writing a custom filter is facilitated by implementing
ZendFilterFilterInterface.
ZendFilterFilterInterface defines a single method, filter(), that may be implemented by user
classes.
The following example demonstrates how to write a custom filter:
1

namespace ApplicationFilter;

2
3

use ZendFilterFilterInterface;

4
5
6
7
8
9

class MyFilter implements FilterInterface
{
public function filter($value)
{
// perform some transformation upon $value to arrive on $valueFiltered

10

return $valueFiltered;

11

}

12
13

}

To attach an instance of the filter defined above to a filter chain:
1
2

$filterChain = new ZendFilterFilterChain();
$filterChain->attach(new ApplicationFilterMyFilter());

503
Zend Framework 2 Documentation, Release 2.2.5

504

Chapter 113. Writing Filters
CHAPTER 114

Introduction to ZendForm

ZendForm is intended primarily as a bridge between your domain models and the View Layer. It composes a thin
layer of objects representing form elements, an InputFilter, and a small number of methods for binding data to and
from the form and attached objects.
The ZendForm component consists of the following objects:
• Elements, which simply consist of a name and attributes.
• Fieldsets, which extend from Elements, but allow composing other fieldsets and elements.
• Forms, which extend from Fieldsets (and thus Elements). They provide data and object binding, and
compose InputFilters. Data binding is done via ZendStdlibHydrator.
To facilitate usage with the view layer, the ZendForm component also aggregates a number of form-specific view
helpers. These accept elements, fieldsets, and/or forms, and use the attributes they compose to render markup.
A small number of specialized elements are provided for accomplishing application-centric tasks. These include the
Csrf element, used to prevent Cross Site Request Forgery attacks, and the Captcha element, used to display and
validate CAPTCHAs.
A Factory is provided to facilitate creation of elements, fieldsets, forms, and the related input filter. The default
Form implementation is backed by a factory to facilitate extension and ease the process of form creation.
The code related to forms can often spread between a variety of concerns: a form definition, an input filter definition, a domain model class, and one or more hydrator implementations. As such, finding the various bits of
code and how they relate can become tedious. To simplify the situation, you can also annotate your domain
model class, detailing the various input filter definitions, attributes, and hydrators that should all be used together.
ZendFormAnnotationAnnotationBuilder can then be used to build the various objects you need.

505
Zend Framework 2 Documentation, Release 2.2.5

506

Chapter 114. Introduction to ZendForm
CHAPTER 115

Form Quick Start

Forms are relatively easy to create. At the bare minimum, each element or fieldset requires a name; typically, you’ll
also provide some attributes to hint to the view layer how it might render the item. The form itself will also typically
compose an InputFilter– which you can also conveniently create directly in the form via a factory. Individual
elements can hint as to what defaults to use when generating a related input for the input filter.
Form validation is as easy as providing an array of data to the setData() method. If you want to simplify your
work even more, you can bind an object to the form; on successful validation, it will be populated from the validated
values.

115.1 Programmatic Form Creation
If nothing else, you can simply start creating elements, fieldsets, and forms and wiring them together.
1
2
3
4
5
6

use
use
use
use
use
use

ZendCaptcha;
ZendFormElement;
ZendFormFieldset;
ZendFormForm;
ZendInputFilterInput;
ZendInputFilterInputFilter;

7
8
9
10
11
12

$name = new Element(’name’);
$name->setLabel(’Your name’);
$name->setAttributes(array(
’type’ => ’text’
));

13
14
15

$email = new ElementEmail(’email’);
$email->setLabel(’Your email address’);

16
17
18
19
20
21

$subject = new Element(’subject’);
$subject->setLabel(’Subject’);
$subject->setAttributes(array(
’type’ => ’text’
));

22
23
24

$message = new ElementTextarea(’message’);
$message->setLabel(’Message’);

507
Zend Framework 2 Documentation, Release 2.2.5

25
26
27
28

$captcha = new ElementCaptcha(’captcha’);
$captcha->setCaptcha(new CaptchaDumb());
$captcha->setLabel(’Please verify you are human’);

29
30

$csrf = new ElementCsrf(’security’);

31
32
33
34
35
36

$send = new Element(’send’);
$send->setValue(’Submit’);
$send->setAttributes(array(
’type’ => ’submit’
));

37
38
39
40
41
42
43
44
45
46

$form = new Form(’contact’);
$form->add($name);
$form->add($email);
$form->add($subject);
$form->add($message);
$form->add($captcha);
$form->add($csrf);
$form->add($send);

47
48
49
50
51

$nameInput = new Input(’name’);
// configure input... and all others
$inputFilter = new InputFilter();
// attach all inputs

52
53

$form->setInputFilter($inputFilter);

As a demonstration of fieldsets, let’s alter the above slightly. We’ll create two fieldsets, one for the sender information,
and another for the message details.
1
2
3

$sender = new Fieldset(’sender’);
$sender->add($name);
$sender->add($email);

4
5
6
7

$details = new Fieldset(’details’);
$details->add($subject);
$details->add($message);

8
9
10
11
12
13
14

$form = new Form(’contact’);
$form->add($sender);
$form->add($details);
$form->add($captcha);
$form->add($csrf);
$form->add($send);

Regardless of approach, as you can see, this can be tedious.

115.2 Creation via Factory
You can create the entire form, and input filter, using the Factory. This is particularly nice if you want to store your
forms as pure configuration; you can simply pass the configuration to the factory and be done.

508

Chapter 115. Form Quick Start
Zend Framework 2 Documentation, Release 2.2.5

1

use ZendFormFactory;

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
47
48
49
50
51
52
53
54
55
56
57
58

$factory = new Factory();
$form
= $factory->createForm(array(
’hydrator’ => ’ZendStdlibHydratorArraySerializable’,
’elements’ => array(
array(
’spec’ => array(
’name’ => ’name’,
’options’ => array(
’label’ => ’Your name’,
),
’type’ => ’Text’,
)
),
array(
’spec’ => array(
’type’ => ’ZendFormElementEmail’,
’name’ => ’email’,
’options’ => array(
’label’ => ’Your email address’,
)
),
),
array(
’spec’ => array(
’name’ => ’subject’,
’options’ => array(
’label’ => ’Subject’,
),
’type’ => ’Text’,
),
),
array(
’spec’ => array(
’type’ => ’ZendFormElementTextarea’,
’name’ => ’message’,
’options’ => array(
’label’ => ’Message’,
)
),
),
array(
’spec’ => array(
’type’ => ’ZendFormElementCaptcha’,
’name’ => ’captcha’,
’options’ => array(
’label’ => ’Please verify you are human.’,
’captcha’ => array(
’class’ => ’Dumb’,
),
),
),
),
array(
’spec’ => array(
’type’ => ’ZendFormElementCsrf’,
’name’ => ’security’,

115.2. Creation via Factory

509
Zend Framework 2 Documentation, Release 2.2.5

),
),
array(
’spec’ => array(
’name’ => ’send’,
’type’ => ’Submit’,
’attributes’ => array(
’value’ => ’Submit’,
),
),
),

59
60
61
62
63
64
65
66
67
68
69

),
/* If we had fieldsets, they’d go here; fieldsets contain
* "elements" and "fieldsets" keys, and potentially a "type"
* key indicating the specific FieldsetInterface
* implementation to use.
’fieldsets’ => array(
),
*/

70
71
72
73
74
75
76
77
78

// Configuration to pass on to
// ZendInputFilterFactory::createInputFilter()
’input_filter’ => array(
/* ... */
),

79
80
81
82
83
84

));

If we wanted to use fieldsets, as we demonstrated in the previous example, we could do the following:
1

use ZendFormFactory;

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

$factory = new Factory();
$form
= $factory->createForm(array(
’hydrator’ => ’ZendStdlibHydratorArraySerializable’,
’fieldsets’ => array(
array(
’spec’ => array(
’name’ => ’sender’,
’elements’ => array(
array(
’spec’ => array(
’name’ => ’name’,
’options’ => array(
’label’ => ’Your name’,
),
’type’ => ’Text’
),
),
array(
’spec’ => array(
’type’ => ’ZendFormElementEmail’,
’name’ => ’email’,
’options’ => array(
’label’ => ’Your email address’,
),
),
),
),
),

510

Chapter 115. Form Quick Start
Zend Framework 2 Documentation, Release 2.2.5

31
32
33
34
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

),
array(
’spec’ => array(
’name’ => ’details’,
’elements’ => array(
array(
’spec’ => array(
’name’ => ’subject’,
’options’ => array(
’label’ => ’Subject’,
),
’type’ => ’Text’,
),
),
array(
’spec’ => array(
’name’ => ’message’,
’type’ => ’ZendFormElementTextarea’,
’options’ => array(
’label’ => ’Message’,
),
),
),
),
),
),
),
’elements’ => array(
array(
’spec’ => array(
’type’ => ’ZendFormElementCaptcha’,
’name’ => ’captcha’,
’options’ => array(
’label’ => ’Please verify you are human. ’,
’captcha’ => array(
’class’ => ’Dumb’,
),
),
),
),
array(
’spec’ => array(
’type’ => ’ZendFormElementCsrf’,
’name’ => ’security’,
),
),
array(
’spec’ => array(
’name’ => ’send’,
’type’ => ’Submit’,
’attributes’ => array(
’value’ => ’Submit’,
),
),
),
),
// Configuration to pass on to
// ZendInputFilterFactory::createInputFilter()

115.2. Creation via Factory

511
Zend Framework 2 Documentation, Release 2.2.5

’input_filter’ => array(
/* ... */
),

89
90
91
92

));

Note that the chief difference is nesting; otherwise, the information is basically the same.
The chief benefits to using the Factory are allowing you to store definitions in configuration, and usage of significant
whitespace.

115.3 Factory-backed Form Extension
The default Form implementation is backed by the Factory. This allows you to extend it, and define your form
internally. This has the benefit of allowing a mixture of programmatic and factory-backed creation, as well as defining
a form for re-use in your application.
1

namespace Contact;

2
3
4
5

use ZendCaptchaAdapterInterface as CaptchaAdapter;
use ZendFormElement;
use ZendFormForm;

6
7
8
9

class ContactForm extends Form
{
protected $captcha;

10

public function __construct(CaptchaAdapter $captcha)
{
$this->captcha = $captcha;

11
12
13
14

// add() can take either an Element/Fieldset instance,
// or a specification, from which the appropriate object
// will be built.

15
16
17
18

$this->add(array(
’name’ => ’name’,
’options’ => array(
’label’ => ’Your name’,
),
’type’ => ’Text’,
));
$this->add(array(
’type’ => ’ZendFormElementEmail’,
’name’ => ’email’,
’options’ => array(
’label’ => ’Your email address’,
),
));
$this->add(array(
’name’ => ’subject’,
’options’ => array(
’label’ => ’Subject’,
),
’type’ => ’Text’,
));
$this->add(array(

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

512

Chapter 115. Form Quick Start
Zend Framework 2 Documentation, Release 2.2.5

’type’ => ’ZendFormElementTextarea’,
’name’ => ’message’,
’options’ => array(
’label’ => ’Message’,
),

41
42
43
44
45

));
$this->add(array(
’type’ => ’ZendFormElementCaptcha’,
’name’ => ’captcha’,
’options’ => array(
’label’ => ’Please verify you are human.’,
’captcha’ => $this->captcha,
),
));
$this->add(new ElementCsrf(’security’));
$this->add(array(
’name’ => ’send’,
’type’ => ’Submit’,
’attributes’ => array(
’value’ => ’Submit’,
),
));

46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

// We could also define the input filter here, or
// lazy-create it in the getInputFilter() method.

64
65

}

66
67

}

You’ll note that this example, the elements are added in the constructor. This is done to allow altering and/or configuring either the form or input filter factory instances, which could then have bearing on how elements, inputs, etc. are
created. In this case, it also allows injection of the CAPTCHA adapter, allowing us to configure it elsewhere in our
application and inject it into the form.

115.4 Validating Forms
Validating forms requires three steps. First, the form must have an input filter attached. Second, you must inject the
data to validate into the form. Third, you validate the form. If invalid, you can retrieve the error messages, if any.
1

$form = new ContactContactForm();

2
3
4

// If the form doesn’t define an input filter by default, inject one.
$form->setInputFilter(new ContactContactFilter());

5
6
7
8

// Get the data. In an MVC application, you might try:
$data = $request->getPost(); // for POST data
$data = $request->getQuery(); // for GET (or query string) data

9
10

$form->setData($data);

11
12
13
14
15
16
17

// Validate the form
if ($form->isValid()) {
$validatedData = $form->getData();
} else {
$messages = $form->getMessages();
}

115.4. Validating Forms

513
Zend Framework 2 Documentation, Release 2.2.5

You can get the raw data if you want, by accessing the composed input filter.
1

$filter = $form->getInputFilter();

2
3
4

$rawValues
= $filter->getRawValues();
$nameRawValue = $filter->getRawValue(’name’);

115.5 Hinting to the Input Filter
Often, you’ll create elements that you expect to behave in the same way on each usage, and for which you’ll want
specific filters or validation as well. Since the input filter is a separate object, how can you achieve these latter points?
Because the default form implementation composes a factory, and the default factory composes an input filter factory,
you can have your elements and/or fieldsets hint to the input filter. If no input or input filter is provided in the input
filter for that element, these hints will be retrieved and used to create them.
To do so,
one of the following must occur.
For elements,
they must implement
ZendInputFilterInputProviderInterface, which defines a getInputSpecification()
method; for fieldsets, they must implement ZendInputFilterInputFilterProviderInterface,
which defines a getInputFilterSpecification() method.
In the case of an element, the getInputSpecification() method should return data to be used by the input
filter factory to create an input. Every HTML5 (email, url, color) elements have a built-in element that use this logic.
For instance, here is how the ZendFormElementColor element is defined:
1

namespace ZendFormElement;

2
3
4
5
6

use
use
use
use

ZendFormElement;
ZendInputFilterInputProviderInterface;
ZendValidatorRegex as RegexValidator;
ZendValidatorValidatorInterface;

7
8
9
10
11
12
13
14
15
16
17

class Color extends Element implements InputProviderInterface
{
/**
* Seed attributes
*
* @var array
*/
protected $attributes = array(
’type’ => ’color’,
);

18

/**
* @var ValidatorInterface
*/
protected $validator;

19
20
21
22
23

/**
* Get validator
*
* @return ValidatorInterface
*/
protected function getValidator()
{
if (null === $this->validator) {
$this->validator = new RegexValidator(’/^#[0-9a-fA-F]{6}$/’);

24
25
26
27
28
29
30
31
32

514

Chapter 115. Form Quick Start
Zend Framework 2 Documentation, Release 2.2.5

}
return $this->validator;

33
34

}

35
36

/**
* Provide default input rules for this element
*
* Attaches an email validator.
*
* @return array
*/
public function getInputSpecification()
{
return array(
’name’ => $this->getName(),
’required’ => true,
’filters’ => array(
array(’name’ => ’ZendFilterStringTrim’),
array(’name’ => ’ZendFilterStringToLower’),
),
’validators’ => array(
$this->getValidator(),
),
);
}

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

}

The above would hint to the input filter to create and attach an input named after the element, marking it as required,
and giving it a StringTrim and StringToLower filters and a Regex validator. Note that you can either rely on
the input filter to create filters and validators, or directly instantiate them.
For fieldsets, you do very similarly; the difference is that getInputFilterSpecification() must return
configuration for an input filter.
1

namespace ContactForm;

2
3
4
5

use ZendFormFieldset;
use ZendInputFilterInputFilterProviderInterface;
use ZendValidator;

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

class SenderFieldset extends Fieldset implements InputFilterProviderInterface
{
public function getInputFilterSpecification()
{
return array(
’name’ => array(
’required’ => true,
’filters’ => array(
array(’name’ => ’ZendFilterStringTrim’),
),
),
’email’ => array(
’required’ => true,
’filters’ => array(
array(’name’ => ’ZendFilterStringTrim’),
),
’validators’ => array(
new ValidatorEmailAddress(),
),

115.5. Hinting to the Input Filter

515
Zend Framework 2 Documentation, Release 2.2.5

),

26

);

27

}

28
29

}

Specifications are a great way to make forms, fieldsets, and elements re-usable trivially in your applications. In fact,
the Captcha and Csrf elements define specifications in order to ensure they can work without additional user
configuration!
Note:
If you set custom input filter specification either in getInputSpecification() or in
getInputFilterSpecification(), the ZendInputFilterInputInterface set for that specific
field is reset to the default ZendInputFilterInput.
Some form elements may need a particular input filter, like ZendFormElementFile: in this case it’s mandatory to specify the type key in your custom specification to match the original one (in ex. for the file element it’s
ZendInputFilterFileInput).

115.6 Binding an object
As noted in the intro, forms in Zend Framework bridge the domain model and the view layer. Let’s see that in action.
When you bind() an object to the form, the following happens:
• The composed Hydrator calls extract() on the object, and uses the values returned, if any, to populate
the value attributes of all elements. If a form contains a fieldset that itself contains another fieldset, the form
will recursively extract the values.
• When isValid() is called, if setData() has not been previously set, the form uses the composed
Hydrator to extract values from the object, and uses those during validation.
• If isValid() is successful (and the bindOnValidate flag is enabled, which is true by default), then the
Hydrator will be passed the validated values to use to hydrate the bound object. (If you do not want this
behavior, call setBindOnValidate(FormInterface::BIND_MANUAL)).
• If the object implements ZendInputFilterInputFilterAwareInterface, the input filter it composes will be used instead of the one composed on the form.
This is easier to understand in practice.
1
2
3

$contact = new ArrayObject;
$contact[’subject’] = ’[Contact Form] ’;
$contact[’message’] = ’Type your message here’;

4
5

$form

= new ContactContactForm;

6
7
8

$form->bind($contact); // form now has default values for
// ’subject’ and ’message’

9
10
11
12
13
14
15

$data = array(
’name’
=> ’John Doe’,
’email’
=> ’j.doe@example.tld’,
’subject’ => ’[Contact Form] ’sup?’,
);
$form->setData($data);

16
17

if ($form->isValid()) {

516

Chapter 115. Form Quick Start
Zend Framework 2 Documentation, Release 2.2.5

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

18
19
20
21
22
23
24
25
26

$contact now looks like:
array(
’name’
=> ’John Doe’,
’email’
=> ’j.doe@example.tld’,
’subject’ => ’[Contact Form] ’sup?’,
’message’ => ’Type your message here’,
)
only as an ArrayObject

}

When an object is bound to the form, calling getData() will return that object by default. If you want to return an
associative array instead, you can pass the FormInterface::VALUES_AS_ARRAY flag to the method.
1
2

use ZendFormFormInterface;
$data = $form->getData(FormInterface::VALUES_AS_ARRAY);

Zend Framework ships several standard hydrators, and implementation is as simple as implementing
ZendStdlibHydratorHydratorInterface, which looks like this:
1

namespace ZendStdlibHydrator;

2
3
4
5
6
7
8

interface HydratorInterface
{
/** @return array */
public function extract($object);
public function hydrate(array $data, $object);
}

115.7 Rendering
As noted previously, forms are meant to bridge the domain model and view layer. We’ve discussed the domain model
binding, but what about the view?
The form component ships a set of form-specific view helpers. These accept the various form objects, and introspect
them in order to generate markup. Typically, they will inspect the attributes, but in special cases, they may look at
other properties and composed objects.
When preparing to render, you will likely want to call prepare(). This method ensures that certain injections are
done, and will likely in the future munge names to allow for scoped[array][notation].
The simplest view helpers available are Form, FormElement, FormLabel, and FormElementErrors. Let’s
use them to display the contact form.
1
2
3
4

<?php
// within a view script
$form = $this->form;
$form->prepare();

5
6
7

// Assuming the "contact/process" route exists...
$form->setAttribute(’action’, $this->url(’contact/process’));

8
9
10

// Set the method attribute for the form
$form->setAttribute(’method’, ’post’);

11
12
13

// Get the form label plugin
$formLabel = $this->plugin(’formLabel’);

14

115.7. Rendering

517
Zend Framework 2 Documentation, Release 2.2.5

15
16
17
18
19
20
21
22
23
24
25

// Render the opening tag
echo $this->form()->openTag($form);
?>
<div class="form_element">
<?php
$name = $form->get(’name’);
echo $formLabel->openTag() . $name->getOption(’label’);
echo $this->formInput($name);
echo $this->formElementErrors($name);
echo $formLabel->closeTag();
?></div>

26
27
28
29
30
31
32
33
34

<div class="form_element">
<?php
$subject = $form->get(’subject’);
echo $formLabel->openTag() . $subject->getOption(’label’);
echo $this->formInput($subject);
echo $this->formElementErrors($subject);
echo $formLabel->closeTag();
?></div>

35
36
37
38
39
40
41
42
43

<div class="form_element">
<?php
$message = $form->get(’message’);
echo $formLabel->openTag() . $message->getOption(’label’);
echo $this->formTextarea($message);
echo $this->formElementErrors($message);
echo $formLabel->closeTag();
?></div>

44
45
46
47
48
49
50
51
52

<div class="form_element">
<?php
$captcha = $form->get(’captcha’);
echo $formLabel->openTag() . $captcha->getOption(’label’);
echo $this->formCaptcha($captcha);
echo $this->formElementErrors($captcha);
echo $formLabel->closeTag();
?></div>

53
54
55

<?php echo $this->formElement($form->get(’security’)) ?>
<?php echo $this->formElement($form->get(’send’)) ?>

56
57

<?php echo $this->form()->closeTag() ?>

There are a few things to note about this. First, to prevent confusion in IDEs and editors when syntax highlighting,
we use helpers to both open and close the form and label tags. Second, there’s a lot of repetition happening here; we
could easily create a partial view script or a composite helper to reduce boilerplate. Third, note that not all elements
are created equal – the CSRF and submit elements don’t need labels or error messages necessarily. Finally, note that
the FormElement helper tries to do the right thing – it delegates actual markup generation to other view helpers;
however, it can only guess what specific form helper to delegate to based on the list it has. If you introduce new form
view helpers, you’ll need to extend the FormElement helper, or create your own.
However, your view files can quickly become long and repetitive to write. While we do not currently provide a singleline form view helper (as this reduces the form customization), the simplest and most recommended way to render your
form is by using the FormRow view helper. This view helper automatically renders a label (if present), the element
itself using the FormElement helper, as well as any errors that could arise. Here is the previous form, rewritten to
take advantage of this helper :

518

Chapter 115. Form Quick Start
Zend Framework 2 Documentation, Release 2.2.5

1
2
3
4

<?php
// within a view script
$form = $this->form;
$form->prepare();

5
6
7

// Assuming the "contact/process" route exists...
$form->setAttribute(’action’, $this->url(’contact/process’));

8
9
10

// Set the method attribute for the form
$form->setAttribute(’method’, ’post’);

11
12
13
14
15
16
17
18
19

// Render the opening tag
echo $this->form()->openTag($form);
?>
<div class="form_element">
<?php
$name = $form->get(’name’);
echo $this->formRow($name);
?></div>

20
21
22
23
24
25

<div class="form_element">
<?php
$subject = $form->get(’subject’);
echo $this->formRow($subject);
?></div>

26
27
28
29
30
31

<div class="form_element">
<?php
$message = $form->get(’message’);
echo $this->formRow($message);
?></div>

32
33
34
35
36
37

<div class="form_element">
<?php
$captcha = $form->get(’captcha’);
echo $this->formRow($captcha);
?></div>

38
39
40

<?php echo $this->formElement($form->get(’security’)) ?>
<?php echo $this->formElement($form->get(’send’)) ?>

41
42

<?php echo $this->form()->closeTag() ?>

Note that FormRow helper automatically prepends the label. If you want it to be rendered after the element itself, you
can pass an optional parameter to the FormRow view helper :
1
2
3
4
5

<div class="form_element">
<?php
$name = $form->get(’name’);
echo $this->formRow($name, **’append’**);
?></div>

115.7.1 Taking advantage of HTML5 input attributes
HTML5 brings a lot of exciting features, one of them being a simplified client form validations. Adding HTML5
attributes is simple as you just need to add specify the attributes. However, please note that adding those attributes
does not automatically add Zend validators to the form’s input filter. You still need to manually add them.
115.7. Rendering

519
Zend Framework 2 Documentation, Release 2.2.5

1
2
3
4
5
6
7
8
9
10
11

$form->add(array(
’name’ => ’phoneNumber’,
’options’ => array(
’label’ => ’Your phone number’
),
’attributes’ => array(
’type’ => ’tel’
’required’ => ’required’,
’pattern’ => ’^0[1-68]([-. ]?[0-9]{2}){4}$’
)
));

View helpers will automatically render those attributes, and hence allowing modern browsers to perform automatic
validation.
Note: Although client validation is nice from a user experience point of view, it has to be used in addition with server
validation, as client validation can be easily fooled.

115.8 Validation Groups
Sometimes you want to validate only a subset of form elements. As an example, let’s say we’re re-using our contact
form over a web service; in this case, the Csrf, Captcha, and submit button elements are not of interest, and
shouldn’t be validated.
ZendForm provides a proxy method to the underlying InputFilter‘s setValidationGroup() method,
allowing us to perform this operation.
1
2
3
4
5
6

$form->setValidationGroup(’name’, ’email’, ’subject’, ’message’);
$form->setData($data);
if ($form->isValid()) {
// Contains only the "name", "email", "subject", and "message" values
$data = $form->getData();
}

If you later want to reset the form to validate all, simply pass the FormInterface::VALIDATE_ALL flag to the
setValidationGroup() method.
1
2

use ZendFormFormInterface;
$form->setValidationGroup(FormInterface::VALIDATE_ALL);

When your form contains nested fieldsets, you can use an array notation to validate only a subset of the fieldsets :
1
2
3
4
5
6
7
8
9
10
11
12

$form->setValidationGroup(array(
’profile’ => array(
’firstname’,
’lastname’
)
));
$form->setData($data);
if ($form->isValid()) {
// Contains only the "firstname" and "lastname" values from the
// "profile" fieldset
$data = $form->getData();
}

520

Chapter 115. Form Quick Start
Zend Framework 2 Documentation, Release 2.2.5

115.9 Using Annotations
Creating a complete forms solution can often be tedious: you’ll create some domain model object, an input filter
for validating it, a form object for providing a representation for it, and potentially a hydrator for mapping the form
elements and fieldsets to the domain model. Wouldn’t it be nice to have a central place to define all of these?
Annotations allow us to solve this problem. You can define the following behaviors with the shipped annotations in
ZendForm:
• AllowEmpty: mark an input as allowing an empty value. This annotation does not require a value.
• Attributes: specify the form, fieldset, or element attributes. This annotation requires an associative array of
values, in a JSON object format: @Attributes({"class":"zend_form","type":"text"}).
• ComposedObject: specify another object with annotations to parse. Typically, this is used if a property references another object, which will then be added to your form as an additional fieldset. Expects a string value
indicating the class for the object being composed.
• ErrorMessage: specify the error message to return for an element in the case of a failed validation. Expects a
string value.
• Exclude: mark a property to exclude from the form or fieldset. This annotation does not require a value.
• Filter: provide a specification for a filter to use on a given element. Expects an associative array of values, with a
“name” key pointing to a string filter name, and an “options” key pointing to an associative array of filter options
for the constructor: @Filter({"name": "Boolean", "options": {"casting":true}}).
This annotation may be specified multiple times.
• Flags: flags to pass to the fieldset or form composing an element or fieldset; these are usually used to specify
the name or priority. The annotation expects an associative array: @Flags({"priority": 100}).
• Hydrator: specify the hydrator class to use for this given form or fieldset. A string value is expected.
• InputFilter: specify the input filter class to use for this given form or fieldset. A string value is expected.
• Input: specify the input class to use for this given element. A string value is expected.
• Name: specify the name of the current element, fieldset, or form. A string value is expected.
• Options: options to pass to the fieldset or form that are used to inform behavior – things that are not attributes;
e.g. labels, CAPTCHA adapters, etc. The annotation expects an associative array: @Options({"label":
"Username:"}).
• Required: indicate whether an element is required. A boolean value is expected. By default, all elements are
required, so this annotation is mainly present to allow disabling a requirement.
• Type: indicate the class to use for the current element, fieldset, or form. A string value is expected.
• Validator: provide a specification for a validator to use on a given element. Expects an associative array of values, with a “name” key pointing to a string validator name, and an “options” key pointing to an associative array
of validator options for the constructor: @Validator({"name": "StringLength", "options":
{"min":3, "max": 25}}). This annotation may be specified multiple times.
To use annotations, you simply include them in your class and/or property docblocks. Annotation names will be
resolved according to the import statements in your class; as such, you can make them as long or as short as you want
depending on what you import.
Note: Form annotations require DoctrineCommon, which contains an annotation parsering engine. The simplest
way to install DoctrineCommon is if you are using Composer; simply update your composer.json and add
the following line to the require section:

115.9. Using Annotations

521
Zend Framework 2 Documentation, Release 2.2.5

1

"doctrine/common": ">=2.1",

Then run php composer.phar update to install the dependency.
If you’re not using Composer, visit the Doctrine project website for more details on installation.
Here’s a simple example.
1

use ZendFormAnnotation;

2
3
4
5
6
7
8
9
10
11
12

/**
* @AnnotationName("user")
* @AnnotationHydrator("ZendStdlibHydratorObjectProperty")
*/
class User
{
/**
* @AnnotationExclude()
*/
public $id;

13

/**
* @AnnotationFilter({"name":"StringTrim"})
* @AnnotationValidator({"name":"StringLength", "options":{"min":1, "max":25}})
* @AnnotationValidator({"name":"Regex", "options":{"pattern":"/^[a-zA-Z][a-zA-Z0-9_-]{0,24}$/"}
* @AnnotationAttributes({"type":"text"})
* @AnnotationOptions({"label":"Username:"})
*/
public $username;

14
15
16
17
18
19
20
21
22

/**
* @AnnotationType("ZendFormElementEmail")
* @AnnotationOptions({"label":"Your email address:"})
*/
public $email;

23
24
25
26
27
28

}

The above will hint to the annotation build to create a form with name “user”, which uses the hydrator
ZendStdlibHydratorObjectProperty. That form will have two elements, “username” and “email”.
The “username” element will have an associated input that has a StringTrim filter, and two validators: a
StringLength validator indicating the username is between 1 and 25 characters, and a Regex validator asserting it follows a specific accepted pattern. The form element itself will have an attribute “type” with value “text” (a text
element), and a label “Username:”. The “email” element will be of type ZendFormElementEmail, and have
the label “Your email address:”.
To use the above, we need ZendFormAnnotationAnnotationBuilder:
1

use ZendFormAnnotationAnnotationBuilder;

2
3
4

$builder = new AnnotationBuilder();
$form
= $builder->createForm(’User’);

At this point, you have a form with the appropriate hydrator attached, an input filter with the appropriate inputs, and
all elements.
Note: You’re not done
In all likelihood, you’ll need to add some more elements to the form you construct. For example, you’ll want a submit

522

Chapter 115. Form Quick Start
Zend Framework 2 Documentation, Release 2.2.5

button, and likely a CSRF-protection element. We recommend creating a fieldset with common elements such as these
that you can then attach to the form you build via annotations.

115.9. Using Annotations

523
Zend Framework 2 Documentation, Release 2.2.5

524

Chapter 115. Form Quick Start
CHAPTER 116

Form Collections

Often, fieldsets or elements in your forms will correspond to other domain objects. In some cases, they may correspond
to collections of domain objects. In this latter case, in terms of user interfaces, you may want to add items dynamically
in the user interface – a great example is adding tasks to a task list.
This document is intended to demonstrate these features. To do so, we first need to define some domain objects that
we’ll be using.
1

namespace ApplicationEntity;

2
3
4
5
6
7
8

class Product
{
/**
* @var string
*/
protected $name;

9
10
11
12
13

/**
* @var int
*/
protected $price;

14
15
16
17
18

/**
* @var Brand
*/
protected $brand;

19
20
21
22
23

/**
* @var array
*/
protected $categories;

24
25
26
27
28
29
30
31
32
33

/**
* @param string $name
* @return Product
*/
public function setName($name)
{
$this->name = $name;
return $this;
}

525
Zend Framework 2 Documentation, Release 2.2.5

34

/**
* @return string
*/
public function getName()
{
return $this->name;
}

35
36
37
38
39
40
41
42

/**
* @param int $price
* @return Product
*/
public function setPrice($price)
{
$this->price = $price;
return $this;
}

43
44
45
46
47
48
49
50
51
52

/**
* @return int
*/
public function getPrice()
{
return $this->price;
}

53
54
55
56
57
58
59
60

/**
* @param Brand $brand
* @return Product
*/
public function setBrand(Brand $brand)
{
$this->brand = $brand;
return $this;
}

61
62
63
64
65
66
67
68
69
70

/**
* @return Brand
*/
public function getBrand()
{
return $this->brand;
}

71
72
73
74
75
76
77
78

/**
* @param array $categories
* @return Product
*/
public function setCategories(array $categories)
{
$this->categories = $categories;
return $this;
}

79
80
81
82
83
84
85
86
87
88

/**
* @return array
*/

89
90
91

526

Chapter 116. Form Collections
Zend Framework 2 Documentation, Release 2.2.5

public function getCategories()
{
return $this->categories;
}

92
93
94
95
96

}

97
98
99
100
101
102
103

class Brand
{
/**
* @var string
*/
protected $name;

104

/**
* @var string
*/
protected $url;

105
106
107
108
109

/**
* @param string $name
* @return Brand
*/
public function setName($name)
{
$this->name = $name;
return $this;
}

110
111
112
113
114
115
116
117
118
119

/**
* @return string
*/
public function getName()
{
return $this->name;
}

120
121
122
123
124
125
126
127

/**
* @param string $url
* @return Brand
*/
public function setUrl($url)
{
$this->url = $url;
return $this;
}

128
129
130
131
132
133
134
135
136
137

/**
* @return string
*/
public function getUrl()
{
return $this->url;
}

138
139
140
141
142
143
144
145

}

146
147
148
149

class Category
{
/**

527
Zend Framework 2 Documentation, Release 2.2.5

* @var string
*/
protected $name;

150
151
152
153

/**
* @param string $name
* @return Category
*/
public function setName($name)
{
$this->name = $name;
return $this;
}

154
155
156
157
158
159
160
161
162
163

/**
* @return string
*/
public function getName()
{
return $this->name;
}

164
165
166
167
168
169
170
171

}

As you can see, this is really simple code. A Product has two scalar properties (name and price), a OneToOne
relationship (one product has one brand), and a OneToMany relationship (one product has many categories).

116.1 Creating Fieldsets
The first step is to create three fieldsets. Each fieldset will contain all the fields and relationships for a specific entity.
Here is the Brand fieldset:
1

namespace ApplicationForm;

2
3
4
5
6

use
use
use
use

ApplicationEntityBrand;
ZendFormFieldset;
ZendInputFilterInputFilterProviderInterface;
ZendStdlibHydratorClassMethods as ClassMethodsHydrator;

7
8
9
10
11
12

class BrandFieldset extends Fieldset implements InputFilterProviderInterface
{
public function __construct()
{
parent::__construct(’brand’);

13

$this
->setHydrator(new ClassMethodsHydrator(false))
->setObject(new Brand())
;

14
15
16
17
18

$this->add(array(
’name’ => ’name’,
’options’ => array(
’label’ => ’Name of the brand’,
),
’attributes’ => array(
’required’ => ’required’,

19
20
21
22
23
24
25

528

Chapter 116. Form Collections
Zend Framework 2 Documentation, Release 2.2.5

),

26

));

27
28

$this->add(array(
’name’ => ’url’,
’type’ => ’ZendFormElementUrl’,
’options’ => array(
’label’ => ’Website of the brand’,
),
’attributes’ => array(
’required’ => ’required’,
),
));

29
30
31
32
33
34
35
36
37
38

}

39
40

/**
* @return array
*/
public function getInputFilterSpecification()
{
return array(
’name’ => array(
’required’ => true,
),
);
}

41
42
43
44
45
46
47
48
49
50
51
52

}

We can discover some new things here. As you can see, the fieldset calls the method setHydrator(), giving it a
ClassMethods hydrator, and the setObject() method, giving it an empty instance of a concrete Brand object.
When the data will be validated, the Form will automatically iterate through all the field sets it contains, and automatically populate the sub-objects, in order to return a complete entity.
Also notice that the Url element has a type of ZendFormElementUrl. This information will be used to
validate the input field. You don’t need to manually add filters or validators for this input as that element provides a
reasonable input specification.
Finally, getInputFilterSpecification() gives the specification for the remaining input (“name”), indicating that this input is required. Note that required in the array “attributes” (when elements are added) is only meant to
add the “required” attribute to the form markup (and therefore has semantic meaning only).
Here is the Category fieldset:
1

namespace ApplicationForm;

2
3
4
5
6

use
use
use
use

ApplicationEntityCategory;
ZendFormFieldset;
ZendInputFilterInputFilterProviderInterface;
ZendStdlibHydratorClassMethods as ClassMethodsHydrator;

7
8
9
10
11
12

class CategoryFieldset extends Fieldset implements InputFilterProviderInterface
{
public function __construct()
{
parent::__construct(’category’);

13
14
15

$this
->setHydrator(new ClassMethodsHydrator(false))

116.1. Creating Fieldsets

529
Zend Framework 2 Documentation, Release 2.2.5

->setObject(new Category())

16

;

17
18

$this->setLabel(’Category’);

19
20

$this->add(array(
’name’ => ’name’,
’options’ => array(
’label’ => ’Name of the category’,
),
’attributes’ => array(
’required’ => ’required’,
),
));

21
22
23
24
25
26
27
28
29

}

30
31

/**
* @return array
*/
public function getInputFilterSpecification()
{
return array(
’name’ => array(
’required’ => true,
),
);
}

32
33
34
35
36
37
38
39
40
41
42
43

}

Nothing new here.
And finally the Product fieldset:
1

namespace ApplicationForm;

2
3
4
5
6

use
use
use
use

ApplicationEntityProduct;
ZendFormFieldset;
ZendInputFilterInputFilterProviderInterface;
ZendStdlibHydratorClassMethods as ClassMethodsHydrator;

7
8
9
10
11
12

class ProductFieldset extends Fieldset implements InputFilterProviderInterface
{
public function __construct()
{
parent::__construct(’product’);

13

$this
->setHydrator(new ClassMethodsHydrator(false))
->setObject(new Product())
;

14
15
16
17
18

$this->add(array(
’name’ => ’name’,
’options’ => array(
’label’ => ’Name of the product’,
),
’attributes’ => array(
’required’ => ’required’,
),

19
20
21
22
23
24
25
26

530

Chapter 116. Form Collections
Zend Framework 2 Documentation, Release 2.2.5

));

27
28

$this->add(array(
’name’ => ’price’,
’options’ => array(
’label’ => ’Price of the product’,
),
’attributes’ => array(
’required’ => ’required’,
),
));

29
30
31
32
33
34
35
36
37
38

$this->add(array(
’type’ => ’ApplicationFormBrandFieldset’,
’name’ => ’brand’,
’options’ => array(
’label’ => ’Brand of the product’,
),
));

39
40
41
42
43
44
45
46

$this->add(array(
’type’ => ’ZendFormElementCollection’,
’name’ => ’categories’,
’options’ => array(
’label’ => ’Please choose categories for this product’,
’count’ => 2,
’should_create_template’ => true,
’allow_add’ => true,
’target_element’ => array(
’type’ => ’ApplicationFormCategoryFieldset’,
),
),
));

47
48
49
50
51
52
53
54
55
56
57
58
59

}

60
61

/**
* Should return an array specification compatible with
* {@link ZendInputFilterFactory::createInputFilter()}.
*
* @return array
*/
public function getInputFilterSpecification()
{
return array(
’name’ => array(
’required’ => true,
),
’price’ => array(
’required’ => true,
’validators’ => array(
array(
’name’ => ’Float’,
),
),
),
);
}

62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

}

116.1. Creating Fieldsets

531
Zend Framework 2 Documentation, Release 2.2.5

We have a lot of new things here!
First, notice how the brand element is added: we specify it to be of type ApplicationFormBrandFieldset.
This is how you handle a OneToOne relationship. When the form is validated, the BrandFieldset will first be
populated, and will return a Brand entity (as we have specified a ClassMethods hydrator, and bound the fieldset
to a Brand entity using the setObject() method). This Brand entity will then be used to populate the Product
entity by calling the setBrand() method.
The next element shows you how to handle OneToMany relationship.
The type is
ZendFormElementCollection, which is a specialized element to handle such cases. As you can
see, the name of the element (“categories”) perfectly matches the name of the property in the Product entity.
This element has a few interesting options:
• count: this is how many times the element (in this case a category) has to be rendered. We’ve set it to two in
this examples.
• should_create_template: if set to true, it will generate a template markup in a <span> element, in
order to simplify adding new element on the fly (we will speak about this one later).
• allow_add: if set to true (which is the default), dynamically added elements will be retrieved and validated;
otherwise, they will be completely ignored. This, of course, depends on what you want to do.
• target_element: this is either an element or, as this is the case in this example, an array that describes the
element or fieldset that will be used in the collection. In this case, the target_element is a Category
fieldset.

116.2 The Form Element
So far, so good. We now have our field sets in place. But those are field sets, not forms. And only Form instances can
be validated. So here is the form :
1

namespace ApplicationForm;

2
3
4
5

use ZendFormForm;
use ZendInputFilterInputFilter;
use ZendStdlibHydratorClassMethods as ClassMethodsHydrator;

6
7
8
9
10
11

class CreateProduct extends Form
{
public function __construct()
{
parent::__construct(’create_product’);

12

$this
->setAttribute(’method’, ’post’)
->setHydrator(new ClassMethodsHydrator(false))
->setInputFilter(new InputFilter())
;

13
14
15
16
17
18

$this->add(array(
’type’ => ’ApplicationFormProductFieldset’,
’options’ => array(
’use_as_base_fieldset’ => true,
),
));

19
20
21
22
23
24
25

$this->add(array(

26

532

Chapter 116. Form Collections
Zend Framework 2 Documentation, Release 2.2.5

’type’ => ’ZendFormElementCsrf’,
’name’ => ’csrf’,

27
28

));

29
30

$this->add(array(
’name’ => ’submit’,
’attributes’ => array(
’type’ => ’submit’,
’value’ => ’Send’,
),
));

31
32
33
34
35
36
37

}

38
39

}

CreateProduct is quite simple, as it only defines a Product fieldset, as well as some other useful fields (CSRF
for security, and a Submit button).
Notice the use_as_base_fieldset option. This option is here to say to the form: “hey, the object I bind to you
is, in fact, bound to the fieldset that is the base fieldset.” This will be to true most of the times.
What’s cool with this approach is that each entity can have its own Fieldset and can be reused. You describe the
elements, the filters, and validators for each entity only once, and the concrete Form instance will only compose those
fieldsets. You no longer have to add the “username” input to every form that deals with users!

116.3 The Controller
Now, let’s create the action in the controller:
1
2
3
4
5
6
7
8

/**
* @return array
*/
public function indexAction()
{
$form = new CreateProduct();
$product = new Product();
$form->bind($product);

9

$request = $this->getRequest();
if ($request->isPost()) {
$form->setData($request->getPost());

10
11
12
13

if ($form->isValid()) {
var_dump($product);
}

14
15
16

}

17
18

return array(
’form’ => $form,
);

19
20
21
22

}

This is super easy. Nothing to do in the controllers. All the magic is done behind the scene.

116.3. The Controller

533
Zend Framework 2 Documentation, Release 2.2.5

116.4 The View
And finally, the view:
1
2
3

<?php
$form->setAttribute(’action’, $this->url(’home’))
->prepare();

4
5

echo $this->form()->openTag($form);

6
7

$product = $form->get(’product’);

8
9
10
11

echo $this->formRow($product->get(’name’));
echo $this->formRow($product->get(’price’));
echo $this->formCollection($product->get(’categories’));

12
13

$brand = $product->get(’brand’);

14
15
16

echo $this->formRow($brand->get(’name’));
echo $this->formRow($brand->get(’url’));

17
18
19

echo $this->formHidden($form->get(’csrf’));
echo $this->formElement($form->get(’submit’));

20
21

echo $this->form()->closeTag();

A few new things here :
• the prepare() method. You must call it prior to rendering anything in the view (this function is only meant
to be called in views, not in controllers).
• the FormRow helper renders a label (if present), the input itself, and errors.
• the FormCollection helper will iterate through every element in the collection, and render every element
with the FormRow helper (you may specify an alternate helper if desired, using the setElementHelper()
method on that FormCollection helper instance). If you need more control about the way you render your
forms, you can iterate through the elements in the collection, and render them manually one by one.
Here is the result:

As you can see, collections are wrapped inside a fieldset, and every item in the collection is itself wrapped in the fieldset. In fact, the Collection element uses label for each item in the collection, while the label of the Collection
element itself is used as the legend of the fieldset. You must have a label on every element in order to use this feature.
If you don’t want the fieldset created, but just the elements within it, simply add a boolean false as the second

534

Chapter 116. Form Collections
Zend Framework 2 Documentation, Release 2.2.5

parameter of the FormCollection view helper.
If you validate, all elements will show errors (this is normal, as we’ve marked them as required). As soon as the form
is valid, this is what we get :

As you can see, the bound object is completely filled, not with arrays, but with objects!
But that’s not all.

116.5 Adding New Elements Dynamically
Remember the should_create_template? We are going to use it now.
Often, forms are not completely static. In our case, let’s say that we don’t want only two categories, but we want the
user to be able to add other ones at runtime. ZendForm has this capability. First, let’s see what it generates when
we ask it to create a template:

As you can see, the collection generates two fieldsets (the two categories) plus a span with a data-template attribute that contains the full HTML code to copy to create a new element in the collection. Of course __index__ (this
is the placeholder generated) has to be changed to a valid value. Currently, we have 2 elements (categories[0]
and categories[1], so __index__ has to be changed to 2.
If you want, this placeholder (__index__ is the default) can be changed using the template_placeholder
option key:
1
2
3
4
5
6
7
8
9

$this->add(array(
’type’ => ’ZendFormElementCollection’,
’name’ => ’categories’,
’options’ => array(
’label’ => ’Please choose categories for this product’,
’count’ => 2,
’should_create_template’ => true,
’template_placeholder’ => ’__placeholder__’,
’target_element’ => array(

116.5. Adding New Elements Dynamically

535
Zend Framework 2 Documentation, Release 2.2.5

’type’ => ’ApplicationFormCategoryFieldset’,

10

),

11

),

12
13

));

First, let’s add a small button “Add new category” anywhere in the form:
1

<button onclick="return add_category()">Add a new category</button>

The add_category function is fairly simple:
1. First, count the number of elements we already have.
2. Get the template from the span‘s data-template attribute.
3. Change the placeholder to a valid index.
4. Add the element to the DOM.
Here is the code:
1
2
3
4
5

<script>
function add_category() {
var currentCount = $(’form > fieldset > fieldset’).length;
var template = $(’form > fieldset > span’).data(’template’);
template = template.replace(/__index__/g, currentCount);

6

$(’form > fieldset’).append(template);

7
8
9
10
11

return false;
}
</script>

(Note: the above example assumes $() is defined, and equivalent to jQuery’s $() function, Dojo’s dojo.query,
etc.)
One small remark about the template.replace: the example uses currentCount and not currentCount
+ 1, as the indices are zero-based (so, if we have two elements in the collection, the third one will have the index 2).
Now, if we validate the form, it will automatically take into account this new element by validating it, filtering it and
retrieving it:

536

Chapter 116. Form Collections
Zend Framework 2 Documentation, Release 2.2.5

Of course, if you don’t want to allow adding elements in a collection, you must set the option allow_add to false.
This way, even if new elements are added, they won’t be validated and hence, not added to the entity. Also, if we don’t
want elements to be added, we don’t need the data template, either. Here’s how you do it:
1
2
3
4
5
6
7
8
9
10
11
12
13

$this->add(array(
’type’ => ’ZendFormElementCollection’,
’name’ => ’categories’,
’options’ => array(
’label’ => ’Please choose categories for this product’,
’count’ => 2,
’should_create_template’ => false,
’allow_add’ => false,
’target_element’ => array(
’type’ => ’ApplicationFormCategoryFieldset’,
),
),
));

There are some limitations to this capability:
• Although you can add new elements and remove them, you CANNOT remove more elements in a collection
than the initial count (for instance, if your code specifies count == 2, you will be able to add a third one
and remove it, but you won’t be able to remove any others. If the initial count is 2, you must have at least two
elements.
• Dynamically added elements have to be added at the end of the collection. They can be added anywhere (these
elements will still be validated and inserted into the entity), but if the validation fails, this newly added element
will be automatically be replaced at the end of the collection.

116.6 Validation groups for fieldsets and collection
Validation groups allow you to validate a subset of fields.
As an example, although the Brand entity has a URL property, we don’t want the user to specify it in the creation
form (but may wish to later in the “Edit Product” form, for instance). Let’s update the view to remove the URL input:

116.6. Validation groups for fieldsets and collection

537
Zend Framework 2 Documentation, Release 2.2.5

1

<?php

2
3
4
5
6

$form
->setAttribute(’action’, $this->url(’home’))
->prepare()
;

7
8

echo $this->form()->openTag($form);

9
10

$product = $form->get(’product’);

11
12
13
14

echo $this->formRow($product->get(’name’));
echo $this->formRow($product->get(’price’));
echo $this->formCollection($product->get(’categories’));

15
16

$brand = $product->get(’brand’);

17
18

echo $this->formRow($brand->get(’name’));

19
20
21

echo $this->formHidden($form->get(’csrf’));
echo $this->formElement($form->get(’submit’));

22
23

echo $this->form()->closeTag();

This is what we get:

The URL input has disappeared, but even if we fill every input, the form won’t validate. In fact, this is normal. We
specified in the input filter that the URL is a required field, so if the form does not have it, it won’t validate, even
though we didn’t add it to the view!
Of course, you could create a BrandFieldsetWithoutURL fieldset, but of course this is not recommended, as a
lot of code will be duplicated.
The solution: validation groups. A validation group is specified in a Form object (hence, in our case, in the

538

Chapter 116. Form Collections
Zend Framework 2 Documentation, Release 2.2.5

CreateProduct form) by giving an array of all the elements we want to validate. Our CreateProduct now
looks like this:
1

namespace ApplicationForm;

2
3
4
5

use ZendFormForm;
use ZendInputFilterInputFilter;
use ZendStdlibHydratorClassMethods as ClassMethodsHydrator;

6
7
8
9
10
11

class CreateProduct extends Form
{
public function __construct()
{
parent::__construct(’create_product’);

12

$this
->setAttribute(’method’, ’post’)
->setHydrator(new ClassMethodsHydrator())
->setInputFilter(new InputFilter())
;

13
14
15
16
17
18

$this->add(array(
’type’ => ’ApplicationFormProductFieldset’,
’options’ => array(
’use_as_base_fieldset’ => true,
),
));

19
20
21
22
23
24
25

$this->add(array(
’type’ => ’ZendFormElementCsrf’,
’name’ => ’csrf’,
));

26
27
28
29
30

$this->add(array(
’name’ => ’submit’,
’attributes’ => array(
’type’ => ’submit’,
’value’ => ’Send’,
),
));

31
32
33
34
35
36
37
38

$this->setValidationGroup(array(
’csrf’,
’product’ => array(
’name’,
’price’,
’brand’ => array(
’name’,
),
’categories’ => array(
’name’,
),
),
));

39
40
41
42
43
44
45
46
47
48
49
50
51

}

52
53

}

Of course, don’t forget to add the CSRF element, as we want it to be validated too (but notice that I didn’t write the
submit element, as we don’t care about it). You can recursively select the elements you want.
116.6. Validation groups for fieldsets and collection

539
Zend Framework 2 Documentation, Release 2.2.5

There is one simple limitation currently: validation groups for collections are set on a per-collection basis, not perelement in a collection basis. This means you cannot say, “validate the name input for the first element of the categories
collection, but don’t validate it for the second one.” But, honestly, this is really an edge-case.
Now, the form validates (and the URL is set to null as we didn’t specify it).

540

Chapter 116. Form Collections
CHAPTER 117

File Uploading

Zend Framework provides support for file uploading by using features in ZendForm, ZendInputFilter,
ZendValidator, ZendFilter, and ZendProgressBar. These reusable framework components provide
a convenient and secure way for handling file uploads in your projects.
Note: If the reader has experience with file uploading in Zend Framework v1.x, he/she will notice some major
differences. Zend_FileTransfer has been deprecated in favor of using the standard ZF2 ZendForm and
ZendInputFilter features.

Note: The file upload features described here are specifically for forms using the POST method. Zend Framework
itself does not currently provide specific support for handling uploads via the PUT method, but it is possible with PHP.
See the PUT Method Support in the PHP documentation for more information.

117.1 Standard Example
Handling file uploads is essentially the same as how you would use ZendForm for form processing, but with some
slight caveats that will be described below.
In this example we will:
• Define a Form for backend validation and filtering.
• Create a view template with a <form> containing a file input.
• Process the form within a Controller action.

117.1.1 The Form and InputFilter
Here we define a ZendFormElementFile input in a Form class named UploadForm.
1

// File: UploadForm.php

2
3
4

use ZendFormElement;
use ZendFormForm;

5

541
Zend Framework 2 Documentation, Release 2.2.5

6
7
8
9
10
11
12

class UploadForm extends Form
{
public function __construct($name = null, $options = array())
{
parent::__construct($name, $options);
$this->addElements();
}

13

public function addElements()
{
// File Input
$file = new ElementFile(’image-file’);
$file->setLabel(’Avatar Image Upload’)
->setAttribute(’id’, ’image-file’);
$this->add($file);
}

14
15
16
17
18
19
20
21
22

}

The File element provides some automatic features that happen behind the scenes:
• The form’s enctype will automatically be set to multipart/form-data when the form prepare()
method is called.
• The file element’s default input specification will create the correct Input type: ZendInputFilterFileInput.
• The FileInput will automatically prepend an UploadFile Validator, to securely validate that the file is actually an uploaded file, and to report other types of upload errors to the user.

117.1.2 The View Template
In the view template we render the <form>, a file input (with label and errors), and a submit button.
1
2
3

// File: upload-form.phtml
<?php $form->prepare(); // The correct enctype is set here ?>
<?php echo $this->form()->openTag($form); ?>

4

<div class="form-element">
<?php $fileElement = $form->get(’image-file’); ?>
<?php echo $this->formLabel($fileElement); ?>
<?php echo $this->formFile($fileElement); ?>
<?php echo $this->formElementErrors($fileElement); ?>
</div>

5
6
7
8
9
10
11

<button>Submit</button>

12
13
14

<?php echo $this->form()->closeTag(); ?>

When rendered, the HTML should look similar to:
1
2
3
4
5

<form name="upload-form" id="upload-form" method="post" enctype="multipart/form-data">
<div class="form-element">
<label for="image-file">Avatar Image Upload</label>
<input type="file" name="image-file" id="image-file">
</div>

6
7
8

<button>Submit</button>
</form>

542

Chapter 117. File Uploading
Zend Framework 2 Documentation, Release 2.2.5

117.1.3 The Controller Action
For the final step, we will instantiate the UploadForm and process any postbacks in a Controller action.
The form processing in the controller action will be similar to normal forms, except that you must merge the $_FILES
information in the request with the other post data.
1

// File: MyController.php

2
3
4
5

public function uploadFormAction()
{
$form = new UploadForm(’upload-form’);

6

$request = $this->getRequest();
if ($request->isPost()) {
// Make certain to merge the files info!
$post = array_merge_recursive(
$request->getPost()->toArray(),
$request->getFiles()->toArray()
);

7
8
9
10
11
12
13
14

$form->setData($post);
if ($form->isValid()) {
$data = $form->getData();
// Form is valid, save the form!
return $this->redirect()->toRoute(’upload-form/success’);
}

15
16
17
18
19
20

}

21
22

return array(’form’ => $form);

23
24

}

Upon a successful file upload, $form->getData() would return:
1
2
3
4
5
6
7
8
9

array(1) {
["image-file"] => array(5) {
["name"]
=> string(11) "myimage.png"
["type"]
=> string(9) "image/png"
["tmp_name"] => string(22) "/private/tmp/phpgRXd58"
["error"]
=> int(0)
["size"]
=> int(14908679)
}
}

Note: It is suggested that you always use the ZendHttpPhpEnvironmentRequest object to retrieve and
merge the $_FILES information with the form, instead of using $_FILES directly.
This is due to how the file information is mapped in the $_FILES array:
1
2
3
4
5
6
7
8
9
10

// A $_FILES array with single input and multiple files:
array(1) {
["image-file"]=>array(2) {
["name"]=>array(2) {
[0]=>string(9)"file0.txt"
[1]=>string(9)"file1.txt"
}
["type"]=>array(2) {
[0]=>string(10)"text/plain"
[1]=>string(10)"text/html"

117.1. Standard Example

543
Zend Framework 2 Documentation, Release 2.2.5

}

11

}

12
13

}

14
15
16
17
18
19
20
21
22
23
24
25
26
27

// How ZendHttpPhpEnvironmentRequest remaps the $_FILES array:
array(1) {
["image-file"]=>array(2) {
[0]=>array(2) {
["name"]=>string(9)"file0.txt"
["type"]=>string(10)"text/plain"
},
[1]=>array(2) {
["name"]=>string(9)"file1.txt"
["type"]=>string(10)"text/html"
}
}
}

ZendInputFilterFileInput expects the file data be in this re-mapped array format.

117.2 File Post-Redirect-Get Plugin
When using other standard form inputs (i.e. text, checkbox, select, etc.) along with file inputs in a Form, you
can encounter a situation where some inputs may become invalid and the user must re-select the file and re-upload.
PHP will delete uploaded files from the temporary directory at the end of the request if it has not been moved away or
renamed. Re-uploading a valid file each time another form input is invalid is inefficient and annoying to users.
One strategy to get around this is to split the form into multiple forms. One form for the file upload inputs and another
for the other standard inputs.
When you cannot separate the forms, the File Post-Redirect-Get Controller Plugin can be used to manage the file
inputs and save off valid uploads until the entire form is valid.
Changing our earlier example to use the fileprg plugin will require two changes.
1. Adding a RenameUpload filter to our form’s file input, with details on where the valid files should be stored:
1

// File: UploadForm.php

2
3
4
5

use ZendInputFilter;
use ZendFormElement;
use ZendFormForm;

6
7
8
9
10
11
12
13
14

class UploadForm extends Form
{
public function __construct($name = null, $options = array())
{
parent::__construct($name, $options);
$this->addElements();
$this->addInputFilter();
}

15
16
17
18
19

544

public function addElements()
{
// File Input
$file = new ElementFile(’image-file’);

Chapter 117. File Uploading
Zend Framework 2 Documentation, Release 2.2.5

$file->setLabel(’Avatar Image Upload’)
->setAttribute(’id’, ’image-file’);
$this->add($file);

20
21
22

}

23
24

public function addInputFilter()
{
$inputFilter = new InputFilterInputFilter();

25
26
27
28

// File Input
$fileInput = new InputFilterFileInput(’image-file’);
$fileInput->setRequired(true);
$fileInput->getFilterChain()->attachByName(
’filerenameupload’,
array(
’target’
=> ’./data/tmpuploads/avatar.png’,
’randomize’ => true,
)
);
$inputFilter->add($fileInput);

29
30
31
32
33
34
35
36
37
38
39
40

$this->setInputFilter($inputFilter);

41

}

42
43

}

The filerenameupload options above would cause an uploaded file to be renamed and moved to:
./data/tmpuploads/avatar_4b3403665fea6.png.
See the RenameUpload filter documentation for more information on its supported options.
2. And, changing the Controller action to use the fileprg plugin:
1

// File: MyController.php

2
3
4
5
6

public function uploadFormAction()
{
$form
= new UploadForm(’upload-form’);
$tempFile = null;

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

$prg = $this->fileprg($form);
if ($prg instanceof ZendHttpPhpEnvironmentResponse) {
return $prg; // Return PRG redirect response
} elseif (is_array($prg)) {
if ($form->isValid()) {
$data = $form->getData();
// Form is valid, save the form!
return $this->redirect()->toRoute(’upload-form/success’);
} else {
// Form not valid, but file uploads might be valid...
// Get the temporary file information to show the user in the view
$fileErrors = $form->get(’image-file’)->getMessages();
if (empty($fileErrors)) {
$tempFile = $form->get(’image-file’)->getValue();
}
}
}

25
26
27

return array(
’form’

=> $form,

117.2. File Post-Redirect-Get Plugin

545
Zend Framework 2 Documentation, Release 2.2.5

’tempFile’ => $tempFile,

28

);

29

}

30

Behind the scenes, the FilePRG plugin will:
• Run the Form’s filters, namely the RenameUpload filter, to move the files out of temporary storage.
• Store the valid POST data in the session across requests.
• Change the required flag of any file inputs that had valid uploads to false. This is so that form resubmissions without uploads will not cause validation errors.
Note: In the case of a partially valid form, it is up to the developer whether to notify the user that files have been
uploaded or not. For example, you may wish to hide the form input and/or display the file information. These things
would be implementation details in the view or in a custom view helper. Just note that neither the FilePRG plugin
nor the formFile view helper will do any automatic notifications or view changes when files have been successfully
uploaded.

117.3 HTML5 Multi-File Uploads
With HTML5 we are able to select multiple files from a single file input using the multiple attribute. Not all
browsers support multiple file uploads, but the file input will safely remain a single file upload for those browsers that
do not support the feature.
To enable multiple file uploads in Zend Framework, just set the file element’s multiple attribute to true:
1

// File: UploadForm.php

2
3
4
5

use ZendInputFilter;
use ZendFormElement;
use ZendFormForm;

6
7
8
9
10
11
12
13
14

class UploadForm extends Form
{
public function __construct($name = null, $options = array())
{
parent::__construct($name, $options);
$this->addElements();
$this->addInputFilter();
}

15

public function addElements()
{
// File Input
$file = new ElementFile(’image-file’);
$file->setLabel(’Avatar Image Upload’)
->setAttribute(’id’, ’image-file’)
->setAttribute(’multiple’, true);
$this->add($file);
}

16
17
18
19
20
21
22
23
24

// That’s it

25

public function addInputFilter()
{
$inputFilter = new InputFilterInputFilter();

26
27
28
29

546

Chapter 117. File Uploading
Zend Framework 2 Documentation, Release 2.2.5

// File Input
$fileInput = new InputFilterFileInput(’image-file’);
$fileInput->setRequired(true);

30
31
32
33

// You only need to define validators and filters
// as if only one file was being uploaded. All files
// will be run through the same validators and filters
// automatically.
$fileInput->getValidatorChain()
->attachByName(’filesize’,
array(’max’ => 204800))
->attachByName(’filemimetype’, array(’mimeType’ => ’image/png,image/x-png’))
->attachByName(’fileimagesize’, array(’maxWidth’ => 100, ’maxHeight’ => 100));

34
35
36
37
38
39
40
41
42

// All files will be renamed, i.e.:
//
./data/tmpuploads/avatar_4b3403665fea6.png,
//
./data/tmpuploads/avatar_5c45147660fb7.png
$fileInput->getFilterChain()->attachByName(
’filerenameupload’,
array(
’target’
=> ’./data/tmpuploads/avatar.png’,
’randomize’ => true,
)
);
$inputFilter->add($fileInput);

43
44
45
46
47
48
49
50
51
52
53
54

$this->setInputFilter($inputFilter);

55

}

56
57

}

You do not need to do anything special with the validators and filters to support multiple file uploads. All of the
files that are uploaded will have the same validators and filters run against them automatically (from logic within
FileInput). You only need to define them as if one file was being uploaded.

117.4 Upload Progress
While pure client-based upload progress meters are starting to become available with HTML5’s Progress Events, not
all browsers have XMLHttpRequest level 2 support. For upload progress to work in a greater number of browsers (IE9
and below), you must use a server-side progress solution.
ZendProgressBarUpload provides handlers that can give you the actual state of a file upload in progress.
To use this feature you need to choose one of the Upload Progress Handlers (APC, uploadprogress, or Session) and
ensure that your server setup has the appropriate extension or feature enabled.
Note: For this example we will use PHP 5.4‘s Session progress handler
PHP 5.4 is required and you may need to verify these php.ini settings for it to work:
file_uploads = On
post_max_size = 50M
upload_max_filesize = 50M
session.upload_progress.enabled = On
session.upload_progress.freq = "1%"
session.upload_progress.min_freq = "1"
; Also make certain ’upload_tmp_dir’ is writable

117.4. Upload Progress

547
Zend Framework 2 Documentation, Release 2.2.5

When uploading a file with a form POST, you must also include the progress identifier in a hidden input. The File
Upload Progress View Helpers provide a convenient way to add the hidden input based on your handler type.
1
2
3
4

// File: upload-form.phtml
<?php $form->prepare(); ?>
<?php echo $this->form()->openTag($form); ?>
<?php echo $this->formFileSessionProgress(); // Must come before the file input! ?>

5

<div class="form-element">
<?php $fileElement = $form->get(’image-file’); ?>
<?php echo $this->formLabel($fileElement); ?>
<?php echo $this->formFile($fileElement); ?>
<?php echo $this->formElementErrors($fileElement); ?>
</div>

6
7
8
9
10
11
12

<button>Submit</button>

13
14
15

<?php echo $this->form()->closeTag(); ?>

When rendered, the HTML should look similar to:
1
2

<form name="upload-form" id="upload-form" method="post" enctype="multipart/form-data">
<input type="hidden" id="progress_key" name="PHP_SESSION_UPLOAD_PROGRESS" value="12345abcde">

3

<div class="form-element">
<label for="image-file">Avatar Image Upload</label>
<input type="file" name="image-file" id="image-file">
</div>

4
5
6
7
8
9
10

<button>Submit</button>
</form>

There are a few different methods for getting progress information to the browser (long vs. short polling). Here we
will use short polling since it is simpler and less taxing on server resources, though keep in mind it is not as responsive
as long polling.
When our form is submitted via AJAX, the browser will continuously poll the server for upload progress.
The following is an example Controller action which provides the progress information:
1

// File: MyController.php

2
3
4
5
6
7
8

public function uploadProgressAction()
{
$id = $this->params()->fromQuery(’id’, null);
$progress = new ZendProgressBarUploadSessionProgress();
return new ZendViewModelJsonModel($progress->getProgress($id));
}

9
10
11
12
13
14
15
16
17

// Returns JSON
//{
//
"total"
//
"current"
//
"rate"
//
"message"
//
"done"
//}

548

:
:
:
:
:

204800,
10240,
1024,
"10kB / 200kB",
false

Chapter 117. File Uploading
Zend Framework 2 Documentation, Release 2.2.5

Warning: This is not the most efficient way of providing upload progress, since each polling request must go
through the Zend Framework bootstrap process. A better example would be to use a standalone php file in the
public folder that bypasses the MVC bootstrapping and only uses the essential ZendProgressBar adapters.
Back in our view template, we will add the JavaScript to perform the AJAX POST of the form data, and to start a
timeout interval for the progress polling. To keep the example code relatively short, we are using the jQuery Form
plugin to do the AJAX form POST. If your project uses a different JavaScript framework (or none at all), this will
hopefully at least illustrate the necessary high-level logic that would need to be performed.
1
2

// File: upload-form.phtml
// ...after the form...

3
4
5
6
7
8
9
10
11

<!-- Twitter Bootstrap progress bar styles:
http://twitter.github.com/bootstrap/components.html#progress -->
<div id="progress" class="help-block">
<div class="progress progress-info progress-striped">
<div class="bar"></div>
</div>
<p></p>
</div>

12
13
14
15
16

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="/js/jquery.form.js"></script>
<script>
var progressInterval;

17
18
19
20
21
22
23
24
25
26
27
28
29
30

function getProgress() {
// Poll our controller action with the progress id
var url = ’/upload-form/upload-progress?id=’ + $(’#progress_key’).val();
$.getJSON(url, function(data) {
if (data.status && !data.status.done) {
var value = Math.floor((data.status.current / data.status.total) * 100);
showProgress(value, ’Uploading...’);
} else {
showProgress(100, ’Complete!’);
clearInterval(progressInterval);
}
});
}

31
32
33
34
35

function startProgress() {
showProgress(0, ’Starting upload...’);
progressInterval = setInterval(getProgress, 900);
}

36
37
38
39
40
41
42
43
44
45
46
47
48

function showProgress(amount, message) {
$(’#progress’).show();
$(’#progress .bar’).width(amount + ’%’);
$(’#progress > p’).html(message);
if (amount < 100) {
$(’#progress .progress’)
.addClass(’progress-info active’)
.removeClass(’progress-success’);
} else {
$(’#progress .progress’)
.removeClass(’progress-info active’)
.addClass(’progress-success’);

117.4. Upload Progress

549
Zend Framework 2 Documentation, Release 2.2.5

}

49
50

}

51
52
53
54
55

$(function() {
// Register a ’submit’ event listener on the form to perform the AJAX POST
$(’#upload-form’).on(’submit’, function(e) {
e.preventDefault();

56

if ($(’#image-file’).val() == ’’) {
// No files selected, abort
return;
}

57
58
59
60
61

// Perform the submit
//$.fn.ajaxSubmit.debug = true;
$(this).ajaxSubmit({
beforeSubmit: function(arr, $form, options) {
// Notify backend that submit is via ajax
arr.push({ name: "isAjax", value: "1" });
},
success: function (response, statusText, xhr, $form) {
clearInterval(progressInterval);
showProgress(100, ’Complete!’);

62
63
64
65
66
67
68
69
70
71
72

// TODO: You’ll need to do some custom logic here to handle a successful
// form post, and when the form is invalid with validation errors.
if (response.status) {
// TODO: Do something with a successful form post, like redirect
// window.location.replace(response.redirect);
} else {
// Clear the file input, otherwise the same file gets re-uploaded
// http://stackoverflow.com/a/1043969
var fileInput = $(’#image-file’);
fileInput.replaceWith( fileInput.val(’’).clone( true ) );

73
74
75
76
77
78
79
80
81
82
83

// TODO: Do something with these errors
// showErrors(response.formErrors);

84
85

}
},
error: function(a, b, c) {
// NOTE: This callback is *not* called when the form is invalid.
// It is called when the browser is unable to initiate or complete the ajax submit.
// You will need to handle validation errors in the ’success’ callback.
console.log(a, b, c);
}

86
87
88
89
90
91
92
93

});
// Start the progress polling
startProgress();

94
95
96
97
98
99

});
});
</script>

And finally, our Controller action can be modified to return form status and validation messages in JSON format if we
see the ‘isAjax’ post parameter (which was set in the JavaScript just before submit):
1

// File: MyController.php

2
3

public function uploadFormAction()

550

Chapter 117. File Uploading
Zend Framework 2 Documentation, Release 2.2.5

4

{
$form = new UploadForm(’upload-form’);

5
6

$request = $this->getRequest();
if ($request->isPost()) {
// Make certain to merge the files info!
$post = array_merge_recursive(
$request->getPost()->toArray(),
$request->getFiles()->toArray()
);

7
8
9
10
11
12
13
14

$form->setData($post);
if ($form->isValid()) {
$data = $form->getData();
// Form is valid, save the form!
if (!empty($post[’isAjax’])) {
return new JsonModel(array(
’status’
=> true,
’redirect’ => $this->url()->fromRoute(’upload-form/success’),
’formData’ => $data,
));
} else {
// Fallback for non-JS clients
return $this->redirect()->toRoute(’upload-form/success’);
}
} else {
if (!empty($post[’isAjax’])) {
// Send back failure information via JSON
return new JsonModel(array(
’status’
=> false,
’formErrors’ => $form->getMessages(),
’formData’
=> $form->getData(),
));
}
}

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

return array(’form’ => $form);

41
42

}

117.5 Additional Info
Related documentation:
• Form File Element
• Form File View Helper
• List of File Validators
• List of File Filters
• File Post-Redirect-Get Controller Plugin
• ZendInputFilterFileInput
• Upload Progress Handlers
• Upload Progress View Helpers
117.5. Additional Info

551
Zend Framework 2 Documentation, Release 2.2.5

External resources and blog posts from the community:
• ZF2FileUploadExamples : A ZF2 module with several file upload examples.

552

Chapter 117. File Uploading
CHAPTER 118

Advanced use of forms

Beginning with Zend Framework 2.1, forms elements can be registered using a designated plugin manager of
ZendServiceManager. This is similar to how view helpers, controller plugins, and filters are registered. This new
feature has a number of benefits, especially when you need to handle complex dependencies in forms/fieldsets. This
section describes all the benefits of this new architecture in ZF 2.1.

118.1 Short names
The first advantage of pulling form elements from the service manager is that now you can use short names to create
new elements through the factory. Therefore, this code:
1
2
3
4

$form->add(array(
’type’ => ’ZendFormElementEmail’,
’name’ => ’email’
));

can now be replaced by:
1
2
3
4

$form->add(array(
’type’ => ’Email’,
’name’ => ’email’
));

Each element provided out-of-the-box by Zend Framework 2 support this natively, so you can now make your initialization code more compact.

118.2 Creating custom elements
ZendForm also supports custom form elements.
To create a custom form element, make it extend the ZendFormElement class, or if you need a more specific
one, extend one of the ZendFormElement classes.
In the following we will show how to create a custom Phone element for entering phone numbers. It will extend
ZendFormElement class and provide some default input rules.
Our custom phone element could look something like this:

553
Zend Framework 2 Documentation, Release 2.2.5

1

namespace ApplicationFormElement;

2
3

use ZendFormElement;

4
5
6
7

use ZendFormElement;
use ZendInputFilterInputProviderInterface;
use ZendValidatorRegex as RegexValidator;

8
9
10
11
12
13
14

class Phone extends Element implements InputProviderInterface
{
/**
* @var ValidatorInterface
*/
protected $validator;

15

/**
* Get a validator if none has been set.
*
* @return ValidatorInterface
*/
public function getValidator()
{
if (null === $this->validator) {
$validator = new RegexValidator(’/^+?d{11,12}$/’);
$validator->setMessage(’Please enter 11 or 12 digits only!’,
RegexValidator::NOT_MATCH);

16
17
18
19
20
21
22
23
24
25
26
27

$this->validator = $validator;

28

}

29
30

return $this->validator;

31

}

32
33

/**
* Sets the validator to use for this element
*
* @param ValidatorInterface $validator
* @return ApplicationFormElementPhone
*/
public function setValidator(ValidatorInterface $validator)
{
$this->validator = $validator;
return $this;
}

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

/**
* Provide default input rules for this element
*
* Attaches a phone number validator.
*
* @return array
*/
public function getInputSpecification()
{
return array(
’name’ => $this->getName(),
’required’ => true,
’filters’ => array(

46
47
48
49
50
51
52
53
54
55
56
57
58

554

Chapter 118. Advanced use of forms
Zend Framework 2 Documentation, Release 2.2.5

array(’name’ => ’ZendFilterStringTrim’),
),
’validators’ => array(
$this->getValidator(),
),

59
60
61
62
63

);

64

}

65
66

}

By implementing the ZendInputFilterInputProviderInterface interface, we are hinting to our form
object that this element provides some default input rules for filtering and/or validating values. In this example the
default input specification provides a ZendFilterStringTrim filter and a ZendValidatorRegex validator that validates that the value optionally has a + sign at the beginning and is followed by 11 or 12 digits.
The easiest way of start using your new custom element in your forms is to use the custom element’s FQCN:
1
2
3
4
5

$form = new ZendFormForm();
$form->add(array(
’name’ => ’phone’,
’type’ => ’ApplicationFormElementPhone’,
));

Or, if you are extending ZendFormForm:
1

namespace ApplicationForm;

2
3

use ZendFormForm;

4
5
6
7
8
9

class MyForm extends Form
{
public function __construct($name = null)
{
parent::__construct($name);

10

$this->add(array(
’name’ => ’phone’,
’type’ => ’ApplicationFormElementPhone’,
))

11
12
13
14

}

15
16

}

If you don’t want to use the custom element’s FQCN, but rather a short name, as of Zend Framework 2.1
you can do so by adding them to the ZendFormFormElementManager plugin manager by utilising the
getFormElementConfig function.
Warning: To use custom elements with the FormElementManager needs a bit more work and most likely a change
in how you write and use your forms.
First, add the custom element to the plugin manager, in your Module.php class:
1

namespace Application;

2
3

use ZendModuleManagerFeatureFormElementProviderInterface;

4
5
6
7
8

class Module implements FormElementProviderInterface
{
public function getFormElementConfig()
{

118.2. Creating custom elements

555
Zend Framework 2 Documentation, Release 2.2.5

return array(
’invokables’ => array(
’phone’ => ’ApplicationFormElementPhone’
)
);

9
10
11
12
13

}

14
15

}

Or, you can do the same in your module.config.php file:
1
2
3
4
5
6
7

return array(
’form_elements’ => array(
’invokables’ => array(
’phone’ => ’ApplicationFormElementPhone’
)
)
);

You can use a factory instead of an invokable in order to handle dependencies in your elements/fieldsets/forms.
And now comes the first catch.
If you are creating your form class by extending ZendFormForm, you must not add the custom element in the
__construct-or (as we have done in the previous example where we used the custom element’s FQCN), but rather
in the init() method:
1

namespace ApplicationForm;

2
3

use ZendFormForm;

4
5
6
7
8
9
10
11
12
13
14

class MyForm extends Form
{
public function init()
{
$this->add(array(
’name’ => ’phone’,
’type’ => ’Phone’,
));
}
}

The second catch is that you must not directly instantiate your form class, but rather get an instance of it through the
ZendFormFormElementManager:
1

namespace ApplicationController;

2
3

use ZendMvcControllerAbstractActionController;

4
5
6
7
8
9
10
11
12
13

class IndexController extends AbstractActionController
{
public function indexAction()
{
$sl = $this->getServiceLocator();
$form = $sl->get(’FormElementManager’)->get(’ApplicationFormMyForm’);
return array(’form’ => $form);
}
}

The biggest gain of this is that you can easily override any built-in Zend Framework form elements if they do not fit
your needs. For instance, if you want to create your own Email element instead of the standard one, you can simply
556

Chapter 118. Advanced use of forms
Zend Framework 2 Documentation, Release 2.2.5

create your element and add it to the form element config with the same key as the element you want to replace:
1

namespace Application;

2
3

use ZendModuleManagerFeatureFormElementProviderInterface;

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

class Module implements FormElementProviderInterface
{
public function getFormElementConfig()
{
return array(
’invokables’ => array(
’Email’ => ’ApplicationFormElementMyEmail’
)
);
}
}

Now, whenever you’ll create an element whose type is ‘Email’, it will create the custom Email element instead of
the built-in one.
Note: if you want to be able to use both the built-in one and your own one, you can still provide the FQCN of the
element, i.e. ZendFormElementEmail.
As you can see here, we first get the form manager (that we modified in our Module.php class), and create
the form by specifying the fully qualified class name of the form. Please note that you don’t need to add
ApplicationFormMyForm to the invokables array. If it is not specified, the form manager will just instantiate it directly.
In short, to create your own form elements (or even reusable fieldsets !) and be able to use them in your form using
the short-name notation, you need to:
1. Create your element (like you did before).
2. Add it to the form element manager by defining the getFormElementConfig, exactly like using
getServiceConfig() and getControllerConfig.
3. Make sure the custom form element is not added in the form’s __construct-or, but rather in it’s init()
method, or after getting an instance of the form.
4. Create your form through the form element manager instead of directly instantiating it.

118.3 Handling dependencies
One of the most complex issues in ZendForm 2.0 was dependency management. For instance, a very frequent use
case is a form that creates a fieldset, that itself need access to the database to populate a Select element. Previously
in such a situation, you would either rely on the Registry using the Singleton pattern, or either you would “transfer”
the dependency from controller to form, and from form to fieldset (and even from fieldset to another fieldset if you
have a complex form). This was ugly and not easy to use. Hopefully, ZendServiceManager solves this use case
in an elegant manner.
For instance, let’s say that a form create a fieldset called AlbumFieldset:
1

namespace ApplicationForm;

2
3

use ZendFormForm;

4

118.3. Handling dependencies

557
Zend Framework 2 Documentation, Release 2.2.5

5
6
7
8
9
10
11
12
13
14

class CreateAlbum extends Form
{
public function init()
{
$this->add(array(
’name’ => ’album’,
’type’ => ’AlbumFieldset’
));
}
}

Let’s now create the AlbumFieldset that depends on an AlbumTable object that allows you to fetch albums
from the database.
1

namespace ApplicationForm;

2
3
4

use AlbumModelAlbumTable;
use ZendFormFieldset;

5
6
7
8
9
10
11
12
13

class AlbumFieldset extends Fieldset
{
public function __construct(AlbumTable $albumTable)
{
// Add any elements that need to fetch data from database
// using the album table !
}
}

For this to work, you need to add a line to the form element manager by adding an element to your Module.php class:
1

namespace Application;

2
3
4

use ApplicationFormAlbumFieldset;
use ZendModuleManagerFeatureFormElementProviderInterface;

5
6
7
8
9
10
11
12
13
14

class Module implements FormElementProviderInterface
{
public function getFormElementConfig()
{
return array(
’factories’ => array(
’AlbumFieldset’ => function($sm) {
// I assume here that the AlbumModelAlbumTable
// dependency have been defined too

15

$serviceLocator = $sm->getServiceLocator();
$albumTable = $serviceLocator->get(’AlbumModelAlbumTable’);
$fieldset = new AlbumFieldset($albumTable);
return $fieldset;

16
17
18
19

}

20

)

21

);

22

}

23
24

}

Create your form using the form element manager instead of directly instantiating it:
1
2

public function testAction()
{

558

Chapter 118. Advanced use of forms
Zend Framework 2 Documentation, Release 2.2.5

$formManager = $this->serviceLocator->get(’FormElementManager’);
$form = $formManager->get(’ApplicationFormCreateAlbum’);

3
4
5

}

Finally, to use your fieldset in a view you need to use the formCollection function.
1
2
3

echo $this->form()->openTag($form);
echo $this->formCollection($form->get(’album’));
echo $this->form()->closeTag();

Et voil! The dependency will be automatically handled by the form element manager, and you don’t need to create the
AlbumTable in your controller, transfer it to the form, which itself passes it over to the fieldset.

118.4 The specific case of initializers
In the previous example, we explicitly defined the dependency in the constructor of the
AlbumFieldset class.
However, in some cases, you may want to use an initializer (like
ZendServiceManagerServiceLocatorAwareInterface) to inject a specific object to all your
forms/fieldsets/elements.
The problem with initializers is that they are injected AFTER the construction of the object, which means that if you
need this dependency when you create elements, it won’t be available yet. For instance, this example won’t work:
1

namespace ApplicationForm;

2
3
4
5
6

use
use
use
use

AlbumModel;
ZendFormFieldset;
ZendServiceManagerServiceLocatorAwareInterface;
ZendServiceManagerServiceLocatorInterface;

7
8
9
10

class AlbumFieldset extends Fieldset implements ServiceLocatorAwareInterface
{
protected $serviceLocator;

11

public function __construct()
{
// Here, $this->serviceLocator is null because it has not been
// injected yet, as initializers are run after __construct
}

12
13
14
15
16
17

public function setServiceLocator(ServiceLocatorInterface $sl)
{
$this->serviceLocator = $sl;
}

18
19
20
21
22

public function getServiceLocator()
{
return $this->serviceLocator;
}

23
24
25
26
27

}

Thankfully, there is an easy workaround:
every form element now implements the new interface
ZendStdlibInitializableInterface, that defines a single init() function. In the context of form
elements, this init() function is automatically called once all the dependencies (including all initializers) are resolved. Therefore, the previous example can be rewritten as such:

118.4. The specific case of initializers

559
Zend Framework 2 Documentation, Release 2.2.5

1

namespace ApplicationForm;

2
3
4
5
6

use
use
use
use

AlbumModel;
ZendFormFieldset;
ZendServiceManagerServiceLocatorAwareInterface;
ZendServiceManagerServiceLocatorInterface;

7
8
9
10

class AlbumFieldset extends Fieldset implements ServiceLocatorAwareInterface
{
protected $serviceLocator;

11

public function init()
{
// Here, we have $this->serviceLocator !!
}

12
13
14
15
16

public function setServiceLocator(ServiceLocatorInterface $sl)
{
$this->serviceLocator = $sl;
}

17
18
19
20
21

public function getServiceLocator()
{
return $this->serviceLocator;
}

22
23
24
25
26

}

560

Chapter 118. Advanced use of forms
CHAPTER 119

Form Elements

119.1 Introduction
A set of specialized elements are provided for accomplishing application-centric tasks. These include several HTML5
input elements with matching server-side validators, the Csrf element (to prevent Cross Site Request Forgery attacks),
and the Captcha element (to display and validate CAPTCHAs).
A Factory is provided to facilitate creation of elements, fieldsets, forms, and the related input filter. See the
ZendForm Quick Start for more information.

119.2 Element Base Class
ZendFormElement is a base class for all specialized elements and ZendFormFieldset.
Basic Usage

At the bare minimum, each element or fieldset requires a name. You will also typically provide some attributes to hint
to the view layer how it might render the item.
1
2

use ZendFormElement;
use ZendFormForm;

3
4
5
6
7
8
9
10

$username = new ElementText(’username’);
$username
->setLabel(’Username’)
->setAttributes(array(
’class’ => ’username’,
’size’ => ’30’,
));

11
12
13
14
15
16
17

$password = new ElementPassword(’password’);
$password
->setLabel(’Password’)
->setAttributes(array(
’size’ => ’30’,
));

561
Zend Framework 2 Documentation, Release 2.2.5

18
19
20
21
22

$form = new Form(’my-form’);
$form
->add($username)
->add($password);

Public Methods

setName(string $name)
Set the name for this element.
getName()
Return the name for this element.
Return type string
setValue(string $value)
Set the value for this element.
getValue()
Return the value for this element.
Return type string
setLabel(string $label)
Set the label content for this element.
getLabel()
Return the label content for this element.
Return type string
setLabelAttributes(array $labelAttributes)
Set the attributes to use with the label.
getLabelAttributes()
Return the attributes to use with the label.
Return type array
setOptions(array $options)
Set options for an element. Accepted options are: "label" and "label_attributes", which call
setLabel and setLabelAttributes, respectively.
getOptions()
Get defined options for an element
Return type array
getOption(string $option)
Return the specified option, if defined. If it’s not defined, returns null.
Return type null|mixed
setAttribute(string $key, mixed $value)
Set a single element attribute.
getAttribute(string $key)
Retrieve a single element attribute.
Return type mixed

562

Chapter 119. Form Elements
Zend Framework 2 Documentation, Release 2.2.5

removeAttribute(string $key)
Remove a single attribute
hasAttribute(string $key)
Check if a specific attribute exists for this element.
Return type boolean
setAttributes(array|Traversable $arrayOrTraversable)
Set many attributes at once. Implementation will decide if this will overwrite or merge.
getAttributes()
Retrieve all attributes at once.
Return type array|Traversable
removeAttributes(array $keys)
Remove many attributes at once
clearAttributes()
Clear all attributes for this element.
setMessages(array|Traversable $messages)
Set a list of messages to report when validation fails.
getMessages()
Returns a list of validation failure messages, if any.
Return type array|Traversable

119.3 Standard Elements
119.3.1 Button
ZendFormElementButton represents a button
ZendFormViewHelperFormButton view helper.

form

input.

It

can

be

used

with

the

ZendFormElementButton extends from ZendFormElement.
Basic Usage

This element automatically adds a type attribute of value button.
1
2

use ZendFormElement;
use ZendFormForm;

3
4
5
6

$button = new ElementButton(’my-button’);
$button->setLabel(’My Button’)
->setValue(’foo’);

7
8
9

$form = new Form(’my-form’);
$form->add($button);

119.3. Standard Elements

563
Zend Framework 2 Documentation, Release 2.2.5

119.3.2 Captcha
ZendFormElementCaptcha can be used with forms where authenticated users are not necessary, but you
want to prevent spam submissions. It is paired with one of the ZendFormViewHelperCaptcha* view
helpers that matches the type of CAPTCHA adapter in use.
Basic Usage

A CAPTCHA adapter must be attached in order for validation to be included in the element’s input filter specification.
See the section on Zend CAPTCHA Adapters for more information on what adapters are available.
1
2
3

use ZendCaptcha;
use ZendFormElement;
use ZendFormForm;

4
5
6
7
8

$captcha = new ElementCaptcha(’captcha’);
$captcha
->setCaptcha(new CaptchaDumb())
->setLabel(’Please verify you are human’);

9
10
11

$form = new Form(’my-form’);
$form->add($captcha);

Here is with the array notation:
1
2

use ZendCaptcha;
use ZendFormForm;

3
4
5
6
7
8
9
10
11
12

$form = new Form(’my-form’);
$form->add(array(
’type’ => ’ZendFormElementCaptcha’,
’name’ => ’captcha’,
’options’ => array(
’label’ => ’Please verify you are human’,
’captcha’ => new CaptchaDumb(),
),
));

Public Methods

The following methods are in addition to the inherited methods of ZendFormElement.
setCaptcha(array|ZendCaptchaAdapterInterface $captcha)
Set
the
CAPTCHA
adapter
for
this
element.
If
$captcha
is
an
array,
ZendCaptchaFactory::factory() will be run to create the adapter from the array configuration.
getCaptcha()
Return the CAPTCHA adapter for this element.
Return type ZendCaptchaAdapterInterface
getInputSpecification()
Returns a input filter specification, which includes a ZendFilterStringTrim filter, and a CAPTCHA
validator.
Return type array

564

Chapter 119. Form Elements
Zend Framework 2 Documentation, Release 2.2.5

119.3.3 Checkbox
ZendFormElementCheckbox is meant to be paired with the ZendFormViewHelperFormCheckbox
for HTML inputs with type checkbox. This element adds an InArray validator to its input filter specification in
order to validate on the server if the checkbox contains either the checked value or the unchecked value.
Basic Usage

This element automatically adds a "type" attribute of value "checkbox".
1
2

use ZendFormElement;
use ZendFormForm;

3
4
5
6
7
8

$checkbox = new ElementCheckbox(’checkbox’);
$checkbox->setLabel(’A checkbox’);
$checkbox->setUseHiddenElement(true);
$checkbox->setCheckedValue("good");
$checkbox->setUncheckedValue("bad");

9
10
11

$form = new Form(’my-form’);
$form->add($checkbox);

Using the array notation:
1

use ZendFormForm;

2
3
4
5
6
7
8
9
10
11
12
13

$form = new Form(’my-form’);
$form->add(array(
’type’ => ’ZendFormElementCheckbox’,
’name’ => ’checkbox’,
’options’ => array(
’label’ => ’A checkbox’,
’use_hidden_element’ => true,
’checked_value’ => ’good’,
’unchecked_value’ => ’bad’
)
));

Public Methods

The following methods are in addition to the inherited methods of ZendFormElement .
setOptions(array $options)
Set options for an element of type Checkbox. Accepted options, in addition to the inherited options of
ZendFormElement , are: "use_hidden_element", "checked_value" and "unchecked_value"
, which call setUseHiddenElement, setCheckedValue and setUncheckedValue , respectively.
setUseHiddenElement(boolean $useHiddenElement)
If set to true (which is default), the view helper will generate a hidden element that contains the unchecked
value. Therefore, when using custom unchecked value, this option have to be set to true.
useHiddenElement()
Return if a hidden element is generated.
Return type boolean

119.3. Standard Elements

565
Zend Framework 2 Documentation, Release 2.2.5

setCheckedValue(string $checkedValue)
Set the value to use when the checkbox is checked.
getCheckedValue()
Return the value used when the checkbox is checked.
Return type string
setUncheckedValue(string $uncheckedValue)
Set the value to use when the checkbox is unchecked.
use_hidden_element is set to true.

For this to work, you must make sure that

getUncheckedValue()
Return the value used when the checkbox is unchecked.
Return type string
getInputSpecification()
Returns a input filter specification, which includes a ZendValidatorInArray to validate if the value is
either checked value or unchecked value.
Return type array
isChecked()
Checks if the checkbox is checked.
Return type boolean
setChecked(bool $value)
Checks or unchecks the checkbox.

119.3.4 Collection
Sometimes, you may want to add input (or a set of inputs) multiple times, either because you don’t want to duplicate
code, or because you do not know in advance how many elements you will need (in the case of elements dynamically
added to a form using JavaScript, for instance). For more information about Collection, please refer to the Form
Collections tutorial.
ZendFormElementCollection is meant to be paired with the ZendFormViewHelperFormCollection.

Basic Usage

1
2

use ZendFormElement;
use ZendFormForm;

3
4
5
6
7
8

$colors = new ElementCollection(’collection’);
$colors->setLabel(’Colors’);
$colors->setCount(2);
$colors->setTargetElement(new ElementColor());
$colors->setShouldCreateTemplate(true);

9
10
11

$form = new Form(’my-form’);
$form->add($colors);

Using the array notation:

566

Chapter 119. Form Elements
Zend Framework 2 Documentation, Release 2.2.5

1
2

use ZendFormElement;
use ZendFormForm;

3
4
5
6
7
8
9
10
11
12
13

$form = new Form(’my-form’);
$form->add(array(
’type’ => ’ZendFormElementCollection’,
’options’ => array(
’label’ => ’Colors’,
’count’ => 2,
’should_create_template’ => true,
’target_element’ => new ElementColor()
)
));

Public Methods

The following methods are in addition to the inherited methods of ZendFormElement .
setOptions(array $options)
Set options for an element of type Collection.
Accepted options, in addition to the inherited options of ZendFormElement , are: "target_element", "count", "allow_add",
"allow_remove", "should_create_template" and "template_placeholder". Those option keys respectively call call setTargetElement, setCount, setAllowAdd, setAllowRemove,
setShouldCreateTemplate and setTemplatePlaceholder.
allowObjectBinding(object $object)
Checks if the object can be set in this fieldset.
Return type bool
setObject(array|Traversable $object)
Set the object used by the hydrator. In this case the “object” is a collection of objects.
populateValues(array|Traversable $data)
Populate values
allowValueBinding()
Checks if this fieldset can bind data
Return type bool
setCount($count)
Defines how many times the target element will
ZendFormViewHelperFormCollection view helper.
getCount()
Return the number of times the target element will
ZendFormViewHelperFormCollection view helper.

be

be

initially

initially

rendered

rendered

by

the

by

the

Return type integer
setTargetElement($elementOrFieldset)
This function either takes an ZendFormElementInterface, ZendFormFieldsetInterface
instance or an array to pass to the form factory. When the Collection element will be validated, the input filter
will be retrieved from this target element and be used to validate each element in the collection.
getTargetElement()
Return the target element used by the collection.
Return type ElementInterface | null
119.3. Standard Elements

567
Zend Framework 2 Documentation, Release 2.2.5

setAllowAdd($allowAdd)
If allowAdd is set to true (which is the default), new elements added dynamically in the form (using JavaScript,
for instance) will also be validated and retrieved.
allowAdd()
Return if new elements can be dynamically added in the collection.
Return type boolean
setAllowRemove($allowRemove)
If allowRemove is set to true (which is the default), new elements added dynamically in the form (using
JavaScript, for instance) will be allowed to be removed.
allowRemove()
Return if new elements can be dynamically removed from the collection.
Return type boolean
setShouldCreateTemplate($shouldCreateTemplate)
If shouldCreateTemplate is set to true (defaults to false), a <span> element will be generated by the
ZendFormViewHelperFormCollection view helper. This non-semantic span element contains
a single data-template HTML5 attribute whose value is the whole HTML to copy to create a new element in the
form. The template is indexed using the templatePlaceholder value.
shouldCreateTemplate()
Return if a template should be created.
Return type boolean
setTemplatePlaceholder($templatePlaceholder)
Set the template placeholder (defaults to __index__) used to index element in the template.
getTemplatePlaceholder()
Returns the template placeholder used to index element in the template.
Return type string
getTemplateElement()
Get a template element used for rendering purposes only
Return type null|ElementInterface|FieldsetInterface
prepareElement()
Prepare the collection by adding a dummy template element if the user want one
prepareFieldset()
If both count and targetElement are set, add them to the fieldset

119.3.5 Csrf
ZendFormElementCsrf pairs with the ZendFormViewHelperFormHidden to provide protection
from CSRF attacks on forms, ensuring the data is submitted by the user session that generated the form and not by a
rogue script. Protection is achieved by adding a hash element to a form and verifying it when the form is submitted.
Basic Usage

This element automatically adds a "type" attribute of value "hidden".

568

Chapter 119. Form Elements
Zend Framework 2 Documentation, Release 2.2.5

1
2

use ZendFormElement;
use ZendFormForm;

3
4

$csrf = new ElementCsrf(’csrf’);

5
6
7

$form = new Form(’my-form’);
$form->add($csrf);

You can change the options of the CSRF validator using the setCsrfValidatorOptions function, or by using
the "csrf_options" key. Here is an example using the array notation:
1

use ZendFormForm;

2
3
4
5
6
7
8
9
10
11
12

$form = new Form(’my-form’);
$form->add(array(
’type’ => ’ZendFormElementCsrf’,
’name’ => ’csrf’,
’options’ => array(
’csrf_options’ => array(
’timeout’ => 600
)
)
));

Public Methods

The following methods are in addition to the inherited methods of ZendFormElement.
getInputSpecification()
Returns a input filter specification, which includes a ZendFilterStringTrim filter and a
ZendValidatorCsrf to validate the CSRF value.
Return type array
setCsrfValidatorOptions(array $options)
Set the options that are used by the CSRF validator.
getCsrfValidatorOptions()
Get the options that are used by the CSRF validator.
Return type array
setCsrfValidator(ZendValidatorCsrf $validator)
Override the default CSRF validator by setting another one.
getCsrfValidator()
Get the CSRF validator.
Return type ZendValidatorCsrf

119.3.6 File
ZendFormElementFile represents a form file input and provides a default input specification with
a type of FileInput (important for handling validators and filters correctly).
It can be used with the
ZendFormViewHelperFormFile view helper.
ZendFormElementFile extends from ZendFormElement.

119.3. Standard Elements

569
Zend Framework 2 Documentation, Release 2.2.5

Basic Usage

This element automatically adds a "type" attribute of value "file". It will also set the form’s enctype to
multipart/form-data during $form->prepare().
1
2

use ZendFormElement;
use ZendFormForm;

3
4
5
6

// Single file upload
$file = new ElementFile(’file’);
$file->setLabel(’Single file input’);

7
8
9
10
11

// HTML5 multiple file upload
$multiFile = new ElementFile(’multi-file’);
$multiFile->setLabel(’Multi file input’)
->setAttribute(’multiple’, true);

12
13
14
15

$form = new Form(’my-file’);
$form->add($file)
->add($multiFile);

119.3.7 Hidden
ZendFormElementHidden represents a hidden
ZendFormViewHelperFormHidden view helper.

form

input.

It

can

be

used

with

the

ZendFormElementHidden extends from ZendFormElement.
Basic Usage

This element automatically adds a "type" attribute of value "hidden".
1
2

use ZendFormElement;
use ZendFormForm;

3
4
5

$hidden = new ElementHidden(’my-hidden’);
$hidden->setValue(’foo’);

6
7
8

$form = new Form(’my-form’);
$form->add($hidden);

Here is with the array notation:
1

use ZendFormForm;

2
3
4
5
6
7
8
9
10

$form = new Form(’my-form’);
$form->add(array(
’type’ => ’ZendFormElementHidden’,
’name’ => ’my-hidden’,
’attributes’ => array(
’value’ => ’foo’
)
));

570

Chapter 119. Form Elements
Zend Framework 2 Documentation, Release 2.2.5

119.3.8 Image
ZendFormElementImage represents a image button form input.
ZendFormViewHelperFormImage view helper.

It can be used with the

ZendFormElementImage extends from ZendFormElement.
Basic Usage

This element automatically adds a "type" attribute of value "image".
1
2

use ZendFormElement;
use ZendFormForm;

3
4
5

$image = new ElementImage(’my-image’);
$image->setAttribute(’src’, ’http://my.image.url’); // Src attribute is required

6
7
8

$form = new Form(’my-form’);
$form->add($image);

119.3.9 Month Select
ZendFormElementMonthSelect
is
meant
to
be
paired
with
the
ZendFormViewHelperFormMonthSelect. This element creates two select elements, where the
first one is populated with months and the second is populated with years. By default, it sets 100 years in the past for
the year element, starting with the current year.
Basic Usage

1
2

use ZendFormElement;
use ZendFormForm;

3
4
5
6

$monthYear = new ElementMonthSelect(’monthyear’);
$monthYear->setLabel(’Select a month and a year’);
$monthYear->setMinYear(1986);

7
8
9

$form = new Form(’dateselect’);
$form->add($monthYear);

Using the array notation:
1

use ZendFormForm;

2
3
4
5
6
7
8
9
10
11

$form = new Form(’dateselect’);
$form->add(array(
’type’ => ’ZendFormElementMonthSelect’,
’name’ => ’monthyear’,
’options’ => array(
’label’ => ’Select a month and a year’,
’min_year’ => 1986,
)
));

119.3. Standard Elements

571
Zend Framework 2 Documentation, Release 2.2.5

Public Methods

The following methods are in addition to the inherited methods of ZendFormElement.
getMonthElement()
Returns the Select element that is used for the months part.
Return type ZendFormElementSelect
getYearElement()
Returns the Select element that is used for the years part.
Return type ZendFormElementSelect
setMonthAttributes(array $monthAttributes)
Set attributes on the Select element that is used for the months part.
getMonthAttributes()
Get attributes on the Select element that is used for the months part.
Return type array
setYearAttributes(array $yearAttributes)
Set attributes on the Select element that is used for the years part.
getYearAttributes()
Get attributes on the Select element that is used for the years part.
Return type array
setMinYear(int $minYear)
Set the minimum year.
getMinYear()
Get the minimum year.
setMaxYear(int $maxYear)
Set the maximum year.
getMaxYear()
Get the maximum year.
setValue(mixed $value)
Set the value for the MonthSelect element.
If the value is an instance of DateTime, it will use the month and year values from that date. Otherwise, the
value should be an associative array with the month key for the month value, and with the year key for the
year value.

119.3.10 MultiCheckbox
ZendFormElementMultiCheckbox
is
meant
to
be
paired
with
the
ZendFormViewHelperFormMultiCheckbox for HTML inputs with type checkbox. This element
adds an InArray validator to its input filter specification in order to validate on the server if the checkbox contains
values from the multiple checkboxes.
Basic Usage

This element automatically adds a "type" attribute of value "checkbox" for every checkboxes.

572

Chapter 119. Form Elements
Zend Framework 2 Documentation, Release 2.2.5

1
2

use ZendFormElement;
use ZendFormForm;

3
4
5
6
7
8
9
10

$multiCheckbox = new ElementMultiCheckbox(’multi-checkbox’);
$multiCheckbox->setLabel(’What do you like ?’);
$multiCheckbox->setValueOptions(array(
’0’ => ’Apple’,
’1’ => ’Orange’,
’2’ => ’Lemon’
));

11
12
13

$form = new Form(’my-form’);
$form->add($multiCheckbox);

Using the array notation:
1

use ZendFormForm;

2
3
4
5
6
7
8
9
10
11
12
13
14
15

$form = new Form(’my-form’);
$form->add(array(
’type’ => ’ZendFormElementMultiCheckbox’,
’name’ => ’multi-checkbox’,
’options’ => array(
’label’ => ’What do you like ?’,
’value_options’ => array(
’0’ => ’Apple’,
’1’ => ’Orange’,
’2’ => ’Lemon’,
),
)
));

Public Methods

The following methods are in addition to the inherited methods of ZendFormElementCheckbox .
setOptions(array $options)
Set options for an element of type Checkbox. Accepted options, in addition to the inherited options of
ZendFormElementCheckbox , are: "value_options", which call setValueOptions.
setValueOptions(array $options)
Set the value options for every checkbox of the multi-checkbox. The array must contain a key => value for
every checkbox.
getValueOptions()
Return the value options.
Return type array

119.3.11 Password
ZendFormElementPassword represents a password form input.
ZendFormViewHelperFormPassword view helper.

It can be used with the

ZendFormElementPassword extends from ZendFormElement.

119.3. Standard Elements

573
Zend Framework 2 Documentation, Release 2.2.5

Basic Usage

This element automatically adds a "type" attribute of value "password".
1
2

use ZendFormElement;
use ZendFormForm;

3
4
5

$password = new ElementPassword(’my-password’);
$password->setLabel(’Enter your password’);

6
7
8

$form = new Form(’my-form’);
$form->add($password);

119.3.12 Radio
ZendFormElementRadio is meant to be paired with the ZendFormViewHelperFormRadio for
HTML inputs with type radio. This element adds an InArray validator to its input filter specification in order to
validate on the server if the value is contains within the radio value elements.
Basic Usage

This element automatically adds a "type" attribute of value "radio" for every radio.
use ZendFormElement;
use ZendFormForm;

1
2
3

$radio = new ElementRadio(’gender’);
$radio->setLabel(’What is your gender ?’);
$radio->setValueOptions(array(
’0’ => ’Female’,
’1’ => ’Male’,
));

4
5
6
7
8
9
10

$form = new Form(’my-form’);
$form->add($radio);

11
12

Using the array notation:
1

use ZendFormForm;

2

$form = new Form(’my-form’);
$form->add(array(
’type’ => ’ZendFormElementRadio’,
’name’ => ’gender’,
’options’ => array(
’label’ => ’What is your gender ?’,
’value_options’ => array(
’0’ => ’Female’,
’1’ => ’Male’,
),
)
));

3
4
5
6
7
8
9
10
11
12
13
14

574

Chapter 119. Form Elements
Zend Framework 2 Documentation, Release 2.2.5

Public Methods

All the methods from the inherited methods of ZendFormElementMultiCheckbox are also available for this element.

119.3.13 Select
ZendFormElementSelect is meant to be paired with the ZendFormViewHelperFormSelect for
HTML inputs with type select. This element adds an InArray validator to its input filter specification in order to
validate on the server if the selected value belongs to the values. This element can be used as a multi-select element
by adding the “multiple” HTML attribute to the element.
Basic Usage

This element automatically adds a "type" attribute of value "select".
1
2

use ZendFormElement;
use ZendFormForm;

3
4
5
6
7
8
9
10
11

$select = new ElementSelect(’language’);
$select->setLabel(’Which is your mother tongue?’);
$select->setValueOptions(array(
’0’ => ’French’,
’1’ => ’English’,
’2’ => ’Japanese’,
’3’ => ’Chinese’,
));

12
13
14

$form = new Form(’language’);
$form->add($select);

Using the array notation:
1

use ZendFormForm;

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

$form = new Form(’my-form’);
$form->add(array(
’type’ => ’ZendFormElementSelect’,
’name’ => ’language’,
’options’ => array(
’label’ => ’Which is your mother tongue?’,
’value_options’ => array(
’0’ => ’French’,
’1’ => ’English’,
’2’ => ’Japanese’,
’3’ => ’Chinese’,
),
)
));

You can add an empty option (option with no value) using the "empty_option" option:
1

use ZendFormForm;

2
3
4
5

$form = new Form(’my-form’);
$form->add(array(
’type’ => ’ZendFormElementSelect’,

119.3. Standard Elements

575
Zend Framework 2 Documentation, Release 2.2.5

’name’ => ’language’,
’options’ => array(
’label’ => ’Which is your mother tongue?’,
’empty_option’ => ’Please choose your language’,
’value_options’ => array(
’0’ => ’French’,
’1’ => ’English’,
’2’ => ’Japanese’,
’3’ => ’Chinese’,
),
)

6
7
8
9
10
11
12
13
14
15
16

));

17

Option groups are also supported. You just need to add an ‘options’ key to the value options.
use ZendFormElement;
use ZendFormForm;

1
2
3

$select = new ElementSelect(’language’);
$select->setLabel(’Which is your mother tongue?’);
$select->setValueOptions(array(
’european’ => array(
’label’ => ’European languages’,
’options’ => array(
’0’ => ’French’,
’1’ => ’Italian’,
),
),
’asian’ => array(
’label’ => ’Asian languages’,
’options’ => array(
’2’ => ’Japanese’,
’3’ => ’Chinese’,
),
),
));

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

$form = new Form(’language’);
$form->add($select);

23
24

Public Methods

The following methods are in addition to the inherited methods of ZendFormElement .
setOptions(array $options)
Set options for an element of type Checkbox. Accepted options, in addition to the inherited options of ZendFormElementCheckbox , are: "value_options" and "empty_option", which call
setValueOptions and setEmptyOption, respectively.
setValueOptions(array $options)
Set the value options for the select element. The array must contain key => value pairs.
getValueOptions()
Return the value options.
Return type array
setEmptyOption($emptyOption)

576

Chapter 119. Form Elements
Zend Framework 2 Documentation, Release 2.2.5

Optionally set a label for an empty option (option with no value). It is set to “null” by default, which means that
no empty option will be rendered.
getEmptyOption()
Get the label for the empty option (null if none).
Return type string|null

119.3.14 Submit
ZendFormElementSubmit represents a submit button form input.
ZendFormViewHelperFormSubmit view helper.

It can be used with the

ZendFormElementSubmit extends from ZendFormElement.
Basic Usage

This element automatically adds a "type" attribute of value "submit".
1
2

use ZendFormElement;
use ZendFormForm;

3
4
5

$submit = new ElementSubmit(’my-submit’);
$submit->setValue(’Submit Form’);

6
7
8

$form = new Form(’my-form’);
$form->add($submit);

119.3.15 Text
ZendFormElementText represents a text
ZendFormViewHelperFormText view helper.

form

input.

It

can

be

used

with

the

ZendFormElementText extends from ZendFormElement.
Basic Usage

This element automatically adds a "type" attribute of value "text".
1
2

use ZendFormElement;
use ZendFormForm;

3
4
5

$text = new ElementText(’my-text’);
$text->setLabel(’Enter your name’);

6
7
8

$form = new Form(’my-form’);
$form->add($text);

119.3.16 Textarea
ZendFormElementTextarea represents a textarea form input.
ZendFormViewHelperFormTextarea view helper.

It can be used with the

ZendFormElementTextarea extends from ZendFormElement.
119.3. Standard Elements

577
Zend Framework 2 Documentation, Release 2.2.5

Basic Usage

This element automatically adds a "type" attribute of value "textarea".
1
2

use ZendFormElement;
use ZendFormForm;

3
4
5

$textarea = new ElementTextarea(’my-textarea’);
$textarea->setLabel(’Enter a description’);

6
7
8

$form = new Form(’my-form’);
$form->add($textarea);

119.4 HTML5 Elements
119.4.1 Color
ZendFormElementColor is meant to be paired with the ZendFormViewHelperFormColor for
HTML5 inputs with type color. This element adds filters and a Regex validator to it’s input filter specification in
order to validate a HTML5 valid simple color value on the server.
Basic Usage

This element automatically adds a "type" attribute of value "color".
1
2

use ZendFormElement;
use ZendFormForm;

3
4
5

$color = new ElementColor(’color’);
$color->setLabel(’Background color’);

6
7
8

$form = new Form(’my-form’);
$form->add($color);

Here is the same example using the array notation:
1

use ZendFormForm;

2
3
4
5
6
7
8
9
10

$form = new Form(’my-form’);
$form->add(array(
’type’ => ’ZendFormElementColor’,
’name’ => ’color’,
’options’ => array(
’label’ => ’Background color’
)
));

Public Methods

The following methods are in addition to the inherited methods of ZendFormElement.
getInputSpecification()
Returns a input filter

578

specification,

which

includes

ZendFilterStringTrim

and

Chapter 119. Form Elements
Zend Framework 2 Documentation, Release 2.2.5

ZendFilterStringToLower filters, and a ZendValidatorRegex to validate the RGB
hex format.
Return type array

119.4.2 Date
ZendFormElementDate is meant to be paired with the ZendFormViewHelperFormDate for
HTML5 inputs with type date. This element adds filters and validators to it’s input filter specification in order to
validate HTML5 date input values on the server.
Basic Usage

This element automatically adds a "type" attribute of value "date".
1
2

use ZendFormElement;
use ZendFormForm;

3
4
5
6
7
8
9
10
11
12
13
14

$date = new ElementDate(’appointment-date’);
$date
->setLabel(’Appointment Date’)
->setAttributes(array(
’min’ => ’2012-01-01’,
’max’ => ’2020-01-01’,
’step’ => ’1’, // days; default step interval is 1 day
))
->setOptions(array(
’format’ => ’Y-m-d’
));

15
16
17

$form = new Form(’my-form’);
$form->add($date);

Here is with the array notation:
1

use ZendFormForm;

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

$form = new Form(’my-form’);
$form->add(array(
’type’ => ’ZendFormElementDate’,
’name’ => ’appointment-date’,
’options’ => array(
’label’ => ’Appointment Date’,
’format’ => ’Y-m-d’
),
’attributes’ => array(
’min’ => ’2012-01-01’,
’max’ => ’2020-01-01’,
’step’ => ’1’, // days; default step interval is 1 day
)
));

Note: Note: the min, max, and step attributes should be set prior to calling ZendForm::prepare(). Otherwise, the
default input specification for the element may not contain the correct validation rules.

119.4. HTML5 Elements

579
Zend Framework 2 Documentation, Release 2.2.5

Public Methods

The following methods are in addition to the inherited methods of ZendFormElementDateTime.
getInputSpecification()
Returns a input filter specification, which includes ZendFilterStringTrim and will add the appropriate
validators based on the values from the min, max, and step attributes and format option. See getInputSpecification in ZendFormElementDateTime for more information.
One difference from ZendFormElementDateTime is that the ZendValidatorDateStep validator will expect the step attribute to use an interval of days (default is 1 day).
Return type array

119.4.3 DateTime
ZendFormElementDateTime is meant to be paired with the ZendFormViewHelperFormDateTime
for HTML5 inputs with type datetime. This element adds filters and validators to it’s input filter specification in order
to validate HTML5 datetime input values on the server.
Basic Usage

This element automatically adds a "type" attribute of value "datetime".
1
2

use ZendFormElement;
use ZendFormForm;

3
4
5
6
7
8
9
10
11
12
13
14

$dateTime = new ElementDateTime(’appointment-date-time’);
$dateTime
->setLabel(’Appointment Date/Time’)
->setAttributes(array(
’min’ => ’2010-01-01T00:00:00Z’,
’max’ => ’2020-01-01T00:00:00Z’,
’step’ => ’1’, // minutes; default step interval is 1 min
))
->setOptions(array(
’format’ => ’Y-m-dTH:iP’
));

15
16
17

$form = new Form(’my-form’);
$form->add($dateTime);

Here is with the array notation:
1

use ZendFormForm;

2
3
4
5
6
7
8
9
10
11
12

$form = new Form(’my-form’);
$form->add(array(
’type’ => ’ZendFormElementDateTime’,
’name’ => ’appointment-date-time’,
’options’ => array(
’label’ => ’Appointment Date/Time’,
’format’ => ’Y-m-dTH:iP’
),
’attributes’ => array(
’min’ => ’2010-01-01T00:00:00Z’,

580

Chapter 119. Form Elements
Zend Framework 2 Documentation, Release 2.2.5

’max’ => ’2020-01-01T00:00:00Z’,
’step’ => ’1’, // minutes; default step interval is 1 min

13
14

)

15
16

));

Note: Note: the min, max, and step attributes should be set prior to calling ZendForm::prepare(). Otherwise, the
default input specification for the element may not contain the correct validation rules.

Public Methods

The following methods are in addition to the inherited methods of ZendFormElement.
getInputSpecification()
Returns a input filter specification, which includes ZendFilterStringTrim and will add the appropriate
validators based on the values from the min, max, and step attributes and format option.
If the min attribute is set, a ZendValidatorGreaterThan validator will be added to ensure the date
value is greater than the minimum value.
If the max attribute is set, a ZendValidatorLessThanValidator validator will be added to ensure
the date value is less than the maximum value.
If the step attribute is set to “any”, step validations will be skipped.
Otherwise, a
ZendValidatorDateStep validator will be added to ensure the date value is within a certain
interval of minutes (default is 1 minute).
The input filter specification also includes a ZendValidatorDate validator to ensure the format of the
value. If the format option is set, that format will be used. Otherwise the default format will be used.
Return type array
setOptions(array $options)
Set options for an element of type DateTime. The accepted option, in addition to the inherited options of
ZendFormElement , is: "format", which calls setFormat.
setFormat(string $format)
Sets the format used to validate the value. Accepts a DateTime compatible string.
getFormat()
Return the DateTime format used to validate the value.
Return type String

119.4.4 DateTimeLocal
ZendFormElementDateTimeLocal
is
meant
to
be
paired
with
the
ZendFormViewHelperFormDateTimeLocal for HTML5 inputs with type datetime-local. This element adds filters and validators to it’s input filter specification in order to validate HTML5 a local datetime input
values on the server.
Basic Usage

This element automatically adds a "type" attribute of value "datetime-local".

119.4. HTML5 Elements

581
Zend Framework 2 Documentation, Release 2.2.5

1
2

use ZendFormElement;
use ZendFormForm;

3
4
5
6
7
8
9
10
11
12
13
14

$dateTimeLocal = new ElementDateTimeLocal(’appointment-date-time’);
$dateTimeLocal
->setLabel(’Appointment Date’)
->setAttributes(array(
’min’ => ’2010-01-01T00:00:00’,
’max’ => ’2020-01-01T00:00:00’,
’step’ => ’1’, // minutes; default step interval is 1 min
))
->setOptions(array(
’format’ => ’Y-m-dTH:i’
));

15
16
17

$form = new Form(’my-form’);
$form->add($dateTimeLocal);

Here is with the array notation:
1

use ZendFormForm;

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

$form = new Form(’my-form’);
$form->add(array(
’type’ => ’ZendFormElementDateTimeLocal’,
’name’ => ’appointment-date-time’,
’options’ => array(
’label’ => ’Appointment Date’,
’format’ => ’Y-m-dTH:i’
),
’attributes’ => array(
’min’ => ’2010-01-01T00:00:00’,
’max’ => ’2020-01-01T00:00:00’,
’step’ => ’1’, // minutes; default step interval is 1 min
)
));

Note: Note: the min, max, and step attributes should be set prior to calling ZendForm::prepare(). Otherwise, the
default input specification for the element may not contain the correct validation rules.

Public Methods

The following methods are in addition to the inherited methods of ZendFormElementDateTime.
getInputSpecification()
Returns a input filter specification, which includes ZendFilterStringTrim and will add the appropriate
validators based on the values from the min, max, and step attributes and format option. See getInputSpecification in ZendFormElementDateTime for more information.
Return type array

119.4.5 Email
ZendFormElementEmail is meant to be paired with the ZendFormViewHelperFormEmail for
HTML5 inputs with type email. This element adds filters and validators to it’s input filter specification in order to
582

Chapter 119. Form Elements
Zend Framework 2 Documentation, Release 2.2.5

validate HTML5 valid email address on the server.
Basic Usage

This element automatically adds a "type" attribute of value "email".
1
2

use ZendFormElement;
use ZendFormForm;

3
4

$form = new Form(’my-form’);

5
6
7
8
9

// Single email address
$email = new ElementEmail(’email’);
$email->setLabel(’Email Address’)
$form->add($email);

10
11
12
13
14
15
16

// Comma separated list of emails
$emails = new ElementEmail(’emails’);
$emails
->setLabel(’Email Addresses’)
->setAttribute(’multiple’, true);
$form->add($emails);

Here is with the array notation:
1

use ZendFormForm;

2
3
4
5
6
7
8
9
10

$form = new Form(’my-form’);
$form->add(array(
’type’ => ’ZendFormElementEmail’,
’name’ => ’email’,
’options’ => array(
’label’ => ’Email Address’
),
));

11
12
13
14
15
16
17
18
19
20
21

$form->add(array(
’type’ => ’ZendFormElementEmail’,
’name’ => ’emails’,
’options’ => array(
’label’ => ’Email Addresses’
),
’attributes’ => array(
’multiple’ => true
)
));

Note: Note: the multiple attribute should be set prior to calling ZendForm::prepare(). Otherwise, the default
input specification for the element may not contain the correct validation rules.

Public Methods

The following methods are in addition to the inherited methods of ZendFormElement.

119.4. HTML5 Elements

583
Zend Framework 2 Documentation, Release 2.2.5

getInputSpecification()
Returns a input filter specification, which includes a ZendFilterStringTrim filter, and a validator
based on the multiple attribute.
If the multiple attribute is unset or false, a ZendValidatorRegex validator will be added to validate
a single email address.
If the multiple attribute is true, a ZendValidatorExplode validator will be added to ensure the input
string value is split by commas before validating each email address with ZendValidatorRegex.
Return type array
setValidator(ValidatorInterface $validator)
Sets the primary validator to use for this element
getValidator()
Get the primary validator
Return type ValidatorInterface
setEmailValidator(ValidatorInterface $validator)
Sets the email validator to use for multiple or single email addresses.
getEmailValidator()
Get the email validator to use for multiple or single email addresses.
The default Regex validator in use is to match that of the browser validation, but you are free to set a different
(more strict) email validator such as ZendValidatorEmail if you wish.

119.4.6 Month
ZendFormElementMonth is meant to be paired with the ZendFormViewHelperFormMonth for
HTML5 inputs with type month. This element adds filters and validators to it’s input filter specification in order to
validate HTML5 month input values on the server.
Basic Usage

This element automatically adds a "type" attribute of value "month".
1
2

use ZendFormElement;
use ZendFormForm;

3
4
5
6
7
8
9
10
11

$month = new ElementMonth(’month’);
$month
->setLabel(’Month’)
->setAttributes(array(
’min’ => ’2012-01’,
’max’ => ’2020-01’,
’step’ => ’1’, // months; default step interval is 1 month
));

12
13
14

$form = new Form
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Zend framework tutorial
Ad

More Related Content

What's hot (17)

Implementing the ibm storwize v3700
Implementing the ibm storwize v3700Implementing the ibm storwize v3700
Implementing the ibm storwize v3700
Diego Alberto Tamayo
 
Tutorial
TutorialTutorial
Tutorial
Luis Fabián Ortega Ruiz
 
AIX 5L Differences Guide Version 5.3 Edition
AIX 5L Differences Guide Version 5.3 EditionAIX 5L Differences Guide Version 5.3 Edition
AIX 5L Differences Guide Version 5.3 Edition
IBM India Smarter Computing
 
Ug893 vivado-ide
Ug893 vivado-ideUg893 vivado-ide
Ug893 vivado-ide
Carmel Jackoby
 
Openobject developer1
Openobject developer1Openobject developer1
Openobject developer1
openerpwiki
 
Openobject developer (2)
Openobject developer (2)Openobject developer (2)
Openobject developer (2)
openerpwiki
 
Open erp openobject-developer
Open erp openobject-developerOpen erp openobject-developer
Open erp openobject-developer
openerpwiki
 
Flask docs
Flask docsFlask docs
Flask docs
Kunal Sangwan
 
R Admin
R AdminR Admin
R Admin
Ajay Ohri
 
Cockpit esp
Cockpit espCockpit esp
Cockpit esp
msabry7
 
Cinelerra Video Editing Manual
Cinelerra Video Editing ManualCinelerra Video Editing Manual
Cinelerra Video Editing Manual
duquoi
 
Tutorial edit
Tutorial editTutorial edit
Tutorial edit
Boris Popov
 
0802 python-tutorial
0802 python-tutorial0802 python-tutorial
0802 python-tutorial
Zahid Hasan
 
Python programming
Python programmingPython programming
Python programming
Keshav Gupta
 
An introduction to tivoli net view for os 390 v1r2 sg245224
An introduction to tivoli net view for os 390 v1r2 sg245224An introduction to tivoli net view for os 390 v1r2 sg245224
An introduction to tivoli net view for os 390 v1r2 sg245224
Banking at Ho Chi Minh city
 
Ibm tivoli intelligent think dynamic orchestrator pre proof of-concept cookbo...
Ibm tivoli intelligent think dynamic orchestrator pre proof of-concept cookbo...Ibm tivoli intelligent think dynamic orchestrator pre proof of-concept cookbo...
Ibm tivoli intelligent think dynamic orchestrator pre proof of-concept cookbo...
Banking at Ho Chi Minh city
 
Migrating to netcool precision for ip networks --best practices for migrating...
Migrating to netcool precision for ip networks --best practices for migrating...Migrating to netcool precision for ip networks --best practices for migrating...
Migrating to netcool precision for ip networks --best practices for migrating...
Banking at Ho Chi Minh city
 
Implementing the ibm storwize v3700
Implementing the ibm storwize v3700Implementing the ibm storwize v3700
Implementing the ibm storwize v3700
Diego Alberto Tamayo
 
Openobject developer1
Openobject developer1Openobject developer1
Openobject developer1
openerpwiki
 
Openobject developer (2)
Openobject developer (2)Openobject developer (2)
Openobject developer (2)
openerpwiki
 
Open erp openobject-developer
Open erp openobject-developerOpen erp openobject-developer
Open erp openobject-developer
openerpwiki
 
Cockpit esp
Cockpit espCockpit esp
Cockpit esp
msabry7
 
Cinelerra Video Editing Manual
Cinelerra Video Editing ManualCinelerra Video Editing Manual
Cinelerra Video Editing Manual
duquoi
 
0802 python-tutorial
0802 python-tutorial0802 python-tutorial
0802 python-tutorial
Zahid Hasan
 
Python programming
Python programmingPython programming
Python programming
Keshav Gupta
 
An introduction to tivoli net view for os 390 v1r2 sg245224
An introduction to tivoli net view for os 390 v1r2 sg245224An introduction to tivoli net view for os 390 v1r2 sg245224
An introduction to tivoli net view for os 390 v1r2 sg245224
Banking at Ho Chi Minh city
 
Ibm tivoli intelligent think dynamic orchestrator pre proof of-concept cookbo...
Ibm tivoli intelligent think dynamic orchestrator pre proof of-concept cookbo...Ibm tivoli intelligent think dynamic orchestrator pre proof of-concept cookbo...
Ibm tivoli intelligent think dynamic orchestrator pre proof of-concept cookbo...
Banking at Ho Chi Minh city
 
Migrating to netcool precision for ip networks --best practices for migrating...
Migrating to netcool precision for ip networks --best practices for migrating...Migrating to netcool precision for ip networks --best practices for migrating...
Migrating to netcool precision for ip networks --best practices for migrating...
Banking at Ho Chi Minh city
 

Similar to Zend framework tutorial (20)

Openobject developer
Openobject developerOpenobject developer
Openobject developer
Ali Mashduqi
 
Openobject developer
Openobject developerOpenobject developer
Openobject developer
openerpwiki
 
W java81
W java81W java81
W java81
rasikow
 
advanced java.pdf
advanced java.pdfadvanced java.pdf
advanced java.pdf
Ali Bozkurt
 
Hdclone
HdcloneHdclone
Hdclone
Shekar Balasubramanian
 
Docker containerization cookbook
Docker containerization cookbookDocker containerization cookbook
Docker containerization cookbook
Pascal Louis
 
Gdfs sg246374
Gdfs sg246374Gdfs sg246374
Gdfs sg246374
Accenture
 
Ref arch for ve sg248155
Ref arch for ve sg248155Ref arch for ve sg248155
Ref arch for ve sg248155
Accenture
 
Java-Design-Patterns.pdf
Java-Design-Patterns.pdfJava-Design-Patterns.pdf
Java-Design-Patterns.pdf
badrfathallah2
 
Advanced-java.pptx
Advanced-java.pptxAdvanced-java.pptx
Advanced-java.pptx
MiltonMolla1
 
VBoxUserManual.pdf
VBoxUserManual.pdfVBoxUserManual.pdf
VBoxUserManual.pdf
vladvah77
 
Embedded linux barco-20121001
Embedded linux barco-20121001Embedded linux barco-20121001
Embedded linux barco-20121001
Marc Leeman
 
An Introduction to Computer Science - python
An Introduction to Computer Science - pythonAn Introduction to Computer Science - python
An Introduction to Computer Science - python
LuisFernandoLozano5
 
0802 python-tutorial
0802 python-tutorial0802 python-tutorial
0802 python-tutorial
urvishathummar1
 
Best Python tutorial (release 3.7.0)
Best Python tutorial (release 3.7.0)Best Python tutorial (release 3.7.0)
Best Python tutorial (release 3.7.0)
youssef bouferdou
 
Python everthing
Python everthingPython everthing
Python everthing
AbdullahAbdullahabdu1
 
Zwcad2011 tutorialbook+
Zwcad2011 tutorialbook+Zwcad2011 tutorialbook+
Zwcad2011 tutorialbook+
sebastian1232009
 
Swi prolog-6.2.6
Swi prolog-6.2.6Swi prolog-6.2.6
Swi prolog-6.2.6
Omar Reyna Angeles
 
eclipse.pdf
eclipse.pdfeclipse.pdf
eclipse.pdf
PerPerso
 
Odoo development
Odoo developmentOdoo development
Odoo development
Ramzi Hajjaji
 
Openobject developer
Openobject developerOpenobject developer
Openobject developer
Ali Mashduqi
 
Openobject developer
Openobject developerOpenobject developer
Openobject developer
openerpwiki
 
W java81
W java81W java81
W java81
rasikow
 
advanced java.pdf
advanced java.pdfadvanced java.pdf
advanced java.pdf
Ali Bozkurt
 
Docker containerization cookbook
Docker containerization cookbookDocker containerization cookbook
Docker containerization cookbook
Pascal Louis
 
Gdfs sg246374
Gdfs sg246374Gdfs sg246374
Gdfs sg246374
Accenture
 
Ref arch for ve sg248155
Ref arch for ve sg248155Ref arch for ve sg248155
Ref arch for ve sg248155
Accenture
 
Java-Design-Patterns.pdf
Java-Design-Patterns.pdfJava-Design-Patterns.pdf
Java-Design-Patterns.pdf
badrfathallah2
 
Advanced-java.pptx
Advanced-java.pptxAdvanced-java.pptx
Advanced-java.pptx
MiltonMolla1
 
VBoxUserManual.pdf
VBoxUserManual.pdfVBoxUserManual.pdf
VBoxUserManual.pdf
vladvah77
 
Embedded linux barco-20121001
Embedded linux barco-20121001Embedded linux barco-20121001
Embedded linux barco-20121001
Marc Leeman
 
An Introduction to Computer Science - python
An Introduction to Computer Science - pythonAn Introduction to Computer Science - python
An Introduction to Computer Science - python
LuisFernandoLozano5
 
Best Python tutorial (release 3.7.0)
Best Python tutorial (release 3.7.0)Best Python tutorial (release 3.7.0)
Best Python tutorial (release 3.7.0)
youssef bouferdou
 
eclipse.pdf
eclipse.pdfeclipse.pdf
eclipse.pdf
PerPerso
 
Ad

Recently uploaded (20)

Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep DiveDesigning Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
ScyllaDB
 
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
BookNet Canada
 
TrsLabs Consultants - DeFi, WEb3, Token Listing
TrsLabs Consultants - DeFi, WEb3, Token ListingTrsLabs Consultants - DeFi, WEb3, Token Listing
TrsLabs Consultants - DeFi, WEb3, Token Listing
Trs Labs
 
Connect and Protect: Networks and Network Security
Connect and Protect: Networks and Network SecurityConnect and Protect: Networks and Network Security
Connect and Protect: Networks and Network Security
VICTOR MAESTRE RAMIREZ
 
Webinar - Top 5 Backup Mistakes MSPs and Businesses Make .pptx
Webinar - Top 5 Backup Mistakes MSPs and Businesses Make   .pptxWebinar - Top 5 Backup Mistakes MSPs and Businesses Make   .pptx
Webinar - Top 5 Backup Mistakes MSPs and Businesses Make .pptx
MSP360
 
AI You Can Trust: The Critical Role of Governance and Quality.pdf
AI You Can Trust: The Critical Role of Governance and Quality.pdfAI You Can Trust: The Critical Role of Governance and Quality.pdf
AI You Can Trust: The Critical Role of Governance and Quality.pdf
Precisely
 
Viam product demo_ Deploying and scaling AI with hardware.pdf
Viam product demo_ Deploying and scaling AI with hardware.pdfViam product demo_ Deploying and scaling AI with hardware.pdf
Viam product demo_ Deploying and scaling AI with hardware.pdf
camilalamoratta
 
Generative Artificial Intelligence (GenAI) in Business
Generative Artificial Intelligence (GenAI) in BusinessGenerative Artificial Intelligence (GenAI) in Business
Generative Artificial Intelligence (GenAI) in Business
Dr. Tathagat Varma
 
MINDCTI revenue release Quarter 1 2025 PR
MINDCTI revenue release Quarter 1 2025 PRMINDCTI revenue release Quarter 1 2025 PR
MINDCTI revenue release Quarter 1 2025 PR
MIND CTI
 
Reimagine How You and Your Team Work with Microsoft 365 Copilot.pptx
Reimagine How You and Your Team Work with Microsoft 365 Copilot.pptxReimagine How You and Your Team Work with Microsoft 365 Copilot.pptx
Reimagine How You and Your Team Work with Microsoft 365 Copilot.pptx
John Moore
 
Q1 2025 Dropbox Earnings and Investor Presentation
Q1 2025 Dropbox Earnings and Investor PresentationQ1 2025 Dropbox Earnings and Investor Presentation
Q1 2025 Dropbox Earnings and Investor Presentation
Dropbox
 
Challenges in Migrating Imperative Deep Learning Programs to Graph Execution:...
Challenges in Migrating Imperative Deep Learning Programs to Graph Execution:...Challenges in Migrating Imperative Deep Learning Programs to Graph Execution:...
Challenges in Migrating Imperative Deep Learning Programs to Graph Execution:...
Raffi Khatchadourian
 
Play It Safe: Manage Security Risks - Google Certificate
Play It Safe: Manage Security Risks - Google CertificatePlay It Safe: Manage Security Risks - Google Certificate
Play It Safe: Manage Security Risks - Google Certificate
VICTOR MAESTRE RAMIREZ
 
Zilliz Cloud Monthly Technical Review: May 2025
Zilliz Cloud Monthly Technical Review: May 2025Zilliz Cloud Monthly Technical Review: May 2025
Zilliz Cloud Monthly Technical Review: May 2025
Zilliz
 
Foundations of Cybersecurity - Google Certificate
Foundations of Cybersecurity - Google CertificateFoundations of Cybersecurity - Google Certificate
Foundations of Cybersecurity - Google Certificate
VICTOR MAESTRE RAMIREZ
 
Vaibhav Gupta BAML: AI work flows without Hallucinations
Vaibhav Gupta BAML: AI work flows without HallucinationsVaibhav Gupta BAML: AI work flows without Hallucinations
Vaibhav Gupta BAML: AI work flows without Hallucinations
john409870
 
UiPath Agentic Automation: Community Developer Opportunities
UiPath Agentic Automation: Community Developer OpportunitiesUiPath Agentic Automation: Community Developer Opportunities
UiPath Agentic Automation: Community Developer Opportunities
DianaGray10
 
The Future of Cisco Cloud Security: Innovations and AI Integration
The Future of Cisco Cloud Security: Innovations and AI IntegrationThe Future of Cisco Cloud Security: Innovations and AI Integration
The Future of Cisco Cloud Security: Innovations and AI Integration
Re-solution Data Ltd
 
How analogue intelligence complements AI
How analogue intelligence complements AIHow analogue intelligence complements AI
How analogue intelligence complements AI
Paul Rowe
 
The No-Code Way to Build a Marketing Team with One AI Agent (Download the n8n...
The No-Code Way to Build a Marketing Team with One AI Agent (Download the n8n...The No-Code Way to Build a Marketing Team with One AI Agent (Download the n8n...
The No-Code Way to Build a Marketing Team with One AI Agent (Download the n8n...
SOFTTECHHUB
 
Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep DiveDesigning Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
ScyllaDB
 
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
BookNet Canada
 
TrsLabs Consultants - DeFi, WEb3, Token Listing
TrsLabs Consultants - DeFi, WEb3, Token ListingTrsLabs Consultants - DeFi, WEb3, Token Listing
TrsLabs Consultants - DeFi, WEb3, Token Listing
Trs Labs
 
Connect and Protect: Networks and Network Security
Connect and Protect: Networks and Network SecurityConnect and Protect: Networks and Network Security
Connect and Protect: Networks and Network Security
VICTOR MAESTRE RAMIREZ
 
Webinar - Top 5 Backup Mistakes MSPs and Businesses Make .pptx
Webinar - Top 5 Backup Mistakes MSPs and Businesses Make   .pptxWebinar - Top 5 Backup Mistakes MSPs and Businesses Make   .pptx
Webinar - Top 5 Backup Mistakes MSPs and Businesses Make .pptx
MSP360
 
AI You Can Trust: The Critical Role of Governance and Quality.pdf
AI You Can Trust: The Critical Role of Governance and Quality.pdfAI You Can Trust: The Critical Role of Governance and Quality.pdf
AI You Can Trust: The Critical Role of Governance and Quality.pdf
Precisely
 
Viam product demo_ Deploying and scaling AI with hardware.pdf
Viam product demo_ Deploying and scaling AI with hardware.pdfViam product demo_ Deploying and scaling AI with hardware.pdf
Viam product demo_ Deploying and scaling AI with hardware.pdf
camilalamoratta
 
Generative Artificial Intelligence (GenAI) in Business
Generative Artificial Intelligence (GenAI) in BusinessGenerative Artificial Intelligence (GenAI) in Business
Generative Artificial Intelligence (GenAI) in Business
Dr. Tathagat Varma
 
MINDCTI revenue release Quarter 1 2025 PR
MINDCTI revenue release Quarter 1 2025 PRMINDCTI revenue release Quarter 1 2025 PR
MINDCTI revenue release Quarter 1 2025 PR
MIND CTI
 
Reimagine How You and Your Team Work with Microsoft 365 Copilot.pptx
Reimagine How You and Your Team Work with Microsoft 365 Copilot.pptxReimagine How You and Your Team Work with Microsoft 365 Copilot.pptx
Reimagine How You and Your Team Work with Microsoft 365 Copilot.pptx
John Moore
 
Q1 2025 Dropbox Earnings and Investor Presentation
Q1 2025 Dropbox Earnings and Investor PresentationQ1 2025 Dropbox Earnings and Investor Presentation
Q1 2025 Dropbox Earnings and Investor Presentation
Dropbox
 
Challenges in Migrating Imperative Deep Learning Programs to Graph Execution:...
Challenges in Migrating Imperative Deep Learning Programs to Graph Execution:...Challenges in Migrating Imperative Deep Learning Programs to Graph Execution:...
Challenges in Migrating Imperative Deep Learning Programs to Graph Execution:...
Raffi Khatchadourian
 
Play It Safe: Manage Security Risks - Google Certificate
Play It Safe: Manage Security Risks - Google CertificatePlay It Safe: Manage Security Risks - Google Certificate
Play It Safe: Manage Security Risks - Google Certificate
VICTOR MAESTRE RAMIREZ
 
Zilliz Cloud Monthly Technical Review: May 2025
Zilliz Cloud Monthly Technical Review: May 2025Zilliz Cloud Monthly Technical Review: May 2025
Zilliz Cloud Monthly Technical Review: May 2025
Zilliz
 
Foundations of Cybersecurity - Google Certificate
Foundations of Cybersecurity - Google CertificateFoundations of Cybersecurity - Google Certificate
Foundations of Cybersecurity - Google Certificate
VICTOR MAESTRE RAMIREZ
 
Vaibhav Gupta BAML: AI work flows without Hallucinations
Vaibhav Gupta BAML: AI work flows without HallucinationsVaibhav Gupta BAML: AI work flows without Hallucinations
Vaibhav Gupta BAML: AI work flows without Hallucinations
john409870
 
UiPath Agentic Automation: Community Developer Opportunities
UiPath Agentic Automation: Community Developer OpportunitiesUiPath Agentic Automation: Community Developer Opportunities
UiPath Agentic Automation: Community Developer Opportunities
DianaGray10
 
The Future of Cisco Cloud Security: Innovations and AI Integration
The Future of Cisco Cloud Security: Innovations and AI IntegrationThe Future of Cisco Cloud Security: Innovations and AI Integration
The Future of Cisco Cloud Security: Innovations and AI Integration
Re-solution Data Ltd
 
How analogue intelligence complements AI
How analogue intelligence complements AIHow analogue intelligence complements AI
How analogue intelligence complements AI
Paul Rowe
 
The No-Code Way to Build a Marketing Team with One AI Agent (Download the n8n...
The No-Code Way to Build a Marketing Team with One AI Agent (Download the n8n...The No-Code Way to Build a Marketing Team with One AI Agent (Download the n8n...
The No-Code Way to Build a Marketing Team with One AI Agent (Download the n8n...
SOFTTECHHUB
 
Ad

Zend framework tutorial

  • 1. Zend Framework 2 Documentation Release 2.2.5 Zend Technologies Ltd. October 31, 2013
  • 3. Contents 1 Overview 1 2 Installation 3 3 Getting Started with Zend Framework 2 3.1 Some assumptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2 The tutorial application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 5 5 4 Getting started: A skeleton application 4.1 Using the Apache Web Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2 Error reporting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 8 9 5 Modules 5.1 Setting up the Album module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2 Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3 Informing the application about our new module . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 11 13 13 6 Routing and controllers 15 7 Create the controller 7.1 Initialise the view scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 18 8 Database and models 8.1 The database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.2 The model files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.3 Using ServiceManager to configure the table gateway and inject into the AlbumTable . 8.4 Back to the controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.5 Listing albums . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 19 19 21 23 23 9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Styling and Translations 10 Forms and actions 10.1 Adding new albums . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.2 Editing an album . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.3 Deleting an album . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 29 29 34 36 i
  • 4. 10.4 Ensuring that the home page displays the list of albums . . . . . . . . . . . . . . . . . . . . . . . . . 11 Conclusion 38 39 12 Zend Framework Tool (ZFTool) 12.1 Installation using Composer . . . . . . . 12.2 Manual installation . . . . . . . . . . . . 12.3 Without installation, using the PHAR file 12.4 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 41 41 41 42 13 Learning Dependency Injection 13.1 Very brief introduction to Di. . . . . . . . . . . . . . . 13.2 Simplest usage case (2 classes, one consumes the other) 13.3 Simplest Usage Case Without Type-hints . . . . . . . . 13.4 Simplest usage case with Compiled Definition . . . . . 13.5 Creating a precompiled definition for others to use . . . 13.6 Using Multiple Definitions From Multiple Sources . . . 13.7 Generating Service Locators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 45 45 47 48 50 50 51 14 Unit Testing a Zend Framework 2 application 14.1 Setting up the tests directory . . . . . . . . . 14.2 Bootstrapping your tests . . . . . . . . . . . 14.3 Your first controller test . . . . . . . . . . . 14.4 A failing test case . . . . . . . . . . . . . . 14.5 Configuring the service manager for the tests 14.6 Testing actions with POST . . . . . . . . . . 14.7 Testing model entities . . . . . . . . . . . . 14.8 Testing model tables . . . . . . . . . . . . . 14.9 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 55 56 58 59 60 61 62 64 68 15 Using the EventManager 15.1 Terminology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.2 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.3 Shared managers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 69 69 71 16 Wildcards 73 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 Listener aggregates 17.1 Introspecting results . . . . . . . . . . . . . . . . . . . . 17.2 Short-ciruiting listener execution . . . . . . . . . . . . . 17.3 Keeping it in order . . . . . . . . . . . . . . . . . . . . . 17.4 Custom event objects . . . . . . . . . . . . . . . . . . . . 17.5 Putting it together: Implementing a simple caching system 17.6 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 76 76 77 78 79 81 18 Advanced Configuration Tricks 18.1 System configuration . . . . . . . 18.2 Module Configuration . . . . . . 18.3 Configuration mapping table . . . 18.4 Configuration Priority . . . . . . 18.5 Configuration merging workflow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 83 87 88 88 88 19 Using ZendNavigation in your Album Module 19.1 Preparation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19.2 Setting Up ZendNavigation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 91 91 ii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  • 5. 19.3 Configuring our Site Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19.4 Adding the Menu View Helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19.5 Adding Breadcrumbs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 Using ZendPaginator in your Album Module 20.1 Preparation . . . . . . . . . . . . . . . . 20.2 Modifying the AlbumTable . . . . . . . 20.3 Modifying the AlbumController . . . . . 20.4 Updating the View Script . . . . . . . . 20.5 Creating the Pagination Control Partial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 93 93 95 . 95 . 98 . 99 . 99 . 100 21 Using the PaginationControl View Helper 103 22 Setting up a database adapter 105 22.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 22.2 Basic setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 22.3 Setting a static adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106 23 Migration from Zend Framework 1 107 24 Namespacing Old Classes 109 24.1 Namespacing a ZF1 Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 24.2 HOWTO Namespace Your Models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 25 Running Zend Framework 2 and Zend Framework 1 in parallel 25.1 Use ZF2 in a ZF1 project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25.2 Use ZF1 in a ZF2 project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25.3 Run ZF1 and ZF2 together . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 113 114 114 26 Introduction to ZendAuthentication 26.1 Adapters . . . . . . . . . . . . 26.2 Results . . . . . . . . . . . . . 26.3 Identity Persistence . . . . . . . 26.4 Usage . . . . . . . . . . . . . . 117 117 118 119 122 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 Database Table Authentication 125 27.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 27.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 27.3 Advanced Usage: Persisting a DbTable Result Object . . . . . . . . . . . . . . . . . . . . . . . . . . 128 28 Digest Authentication 28.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28.2 Specifics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28.3 Identity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 131 131 131 29 HTTP Authentication Adapter 29.1 Introduction . . . . . . . 29.2 Design Overview . . . . . 29.3 Configuration Options . . 29.4 Resolvers . . . . . . . . . 29.5 Basic Usage . . . . . . . . . . . . 133 133 133 134 134 135 30 LDAP Authentication 30.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30.2 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30.3 The API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 137 137 139 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iii
  • 6. 30.4 Server Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 30.5 Collecting Debugging Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142 30.6 Common Options for Specific Servers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142 31 Authentication Validator 145 31.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 31.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 32 Introduction to ZendBarcode 147 33 Barcode creation using ZendBarcodeBarcode class 149 33.1 Using ZendBarcodeBarcode::factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149 33.2 Drawing a barcode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 33.3 Rendering a barcode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 34 ZendBarcodeBarcode Objects 153 34.1 Common Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 34.2 Common Additional Getters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 34.3 Description of shipped barcodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 35 ZendBarcode Renderers 163 35.1 Common Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 35.2 ZendBarcodeRendererImage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164 35.3 ZendBarcodeRendererPdf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164 36 ZendCacheStorageAdapter 36.1 Overview . . . . . . . . . . . . . . . 36.2 Quick Start . . . . . . . . . . . . . . 36.3 Basic Configuration Options . . . . . 36.4 The StorageInterface . . . . . . . . . 36.5 The AvailableSpaceCapableInterface 36.6 The TotalSpaceCapableInterface . . . 36.7 The ClearByNamespaceInterface . . 36.8 The ClearByPrefixInterface . . . . . 36.9 The ClearExpiredInterface . . . . . . 36.10 The FlushableInterface . . . . . . . . 36.11 The IterableInterface . . . . . . . . . 36.12 The OptimizableInterface . . . . . . 36.13 The TaggableInterface . . . . . . . . 36.14 The Apc Adapter . . . . . . . . . . . 36.15 The Dba Adapter . . . . . . . . . . . 36.16 The Filesystem Adapter . . . . . . . 36.17 The Memcached Adapter . . . . . . 36.18 The Memory Adapter . . . . . . . . 36.19 The WinCache Adapter . . . . . . . 36.20 The XCache Adapter . . . . . . . . . 36.21 The ZendServerDisk Adapter . . . . 36.22 The ZendServerShm Adapter . . . . 36.23 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165 165 165 166 166 168 168 169 169 169 169 169 170 170 170 171 172 173 174 175 176 177 177 178 37 ZendCacheStorageCapabilities 37.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37.2 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37.3 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181 181 181 183 iv . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  • 7. 38 ZendCacheStoragePlugin 38.1 Overview . . . . . . . . . . . . . 38.2 Quick Start . . . . . . . . . . . . 38.3 The ClearExpiredByFactor Plugin 38.4 The ExceptionHandler Plugin . . 38.5 The IgnoreUserAbort Plugin . . . 38.6 The OptimizeByFactor Plugin . . 38.7 The Serializer Plugin . . . . . . . 38.8 Available Methods . . . . . . . . 38.9 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185 185 185 186 186 186 186 187 187 188 39 ZendCachePattern 189 39.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 39.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 39.3 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190 40 ZendCachePatternCallbackCache 40.1 Overview . . . . . . . . . . . . 40.2 Quick Start . . . . . . . . . . . 40.3 Configuration Options . . . . . 40.4 Available Methods . . . . . . . 40.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 191 191 191 192 192 41 ZendCachePatternClassCache 41.1 Overview . . . . . . . . . . 41.2 Quick Start . . . . . . . . . 41.3 Configuration Options . . . 41.4 Available Methods . . . . . 41.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 193 193 193 194 194 42 ZendCachePatternObjectCache 42.1 Overview . . . . . . . . . . . 42.2 Quick Start . . . . . . . . . . 42.3 Configuration Options . . . . 42.4 Available Methods . . . . . . 42.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197 197 197 198 198 199 43 ZendCachePatternOutputCache 43.1 Overview . . . . . . . . . . . 43.2 Quick Start . . . . . . . . . . 43.3 Configuration Options . . . . 43.4 Available Methods . . . . . . 43.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201 201 201 201 201 202 44 ZendCachePatternCaptureCache 44.1 Overview . . . . . . . . . . . . 44.2 Quick Start . . . . . . . . . . . 44.3 Configuration Options . . . . . 44.4 Available Methods . . . . . . . 44.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203 203 203 204 204 205 45 Introduction to ZendCaptcha 207 46 Captcha Operation 209 v
  • 8. 47 CAPTCHA Adapters 47.1 ZendCaptchaAbstractWord 47.2 ZendCaptchaDumb . . . . 47.3 ZendCaptchaFiglet . . . . 47.4 ZendCaptchaImage . . . . 47.5 ZendCaptchaReCaptcha . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211 211 212 212 212 213 48 Introduction to ZendConfig 215 48.1 Using ZendConfigConfig with a Reader Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 48.2 Using ZendConfigConfig with a PHP Configuration File . . . . . . . . . . . . . . . . . . . . . . . 216 49 Theory of Operation 50 ZendConfigReader 50.1 ZendConfigReaderIni . 50.2 ZendConfigReaderXml 50.3 ZendConfigReaderJson 50.4 ZendConfigReaderYaml 217 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219 219 220 222 223 51 ZendConfigWriter 51.1 ZendConfigWriterIni . . . . 51.2 ZendConfigWriterXml . . . 51.3 ZendConfigWriterPhpArray 51.4 ZendConfigWriterJson . . . 51.5 ZendConfigWriterYaml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225 225 226 227 228 228 52 ZendConfigProcessor 52.1 ZendConfigProcessorConstant . 52.2 ZendConfigProcessorFilter . . 52.3 ZendConfigProcessorQueue . . 52.4 ZendConfigProcessorToken . . 52.5 ZendConfigProcessorTranslator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231 231 232 232 233 233 . . . . 53 The Factory 235 53.1 Loading configuration file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235 53.2 Storing configuration file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235 54 Introduction to ZendConsole 237 54.1 Writing console routes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237 54.2 Handling console requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239 54.3 Adding console usage info . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240 55 Console routes and routing 55.1 Router configuration . . . 55.2 Basic route . . . . . . . . 55.3 Catchall route . . . . . . . 55.4 Console routes cheat-sheet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243 243 244 248 249 56 Console-aware modules 251 56.1 Application banner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251 56.2 Usage information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253 56.3 Best practices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259 57 Console-aware action controllers 261 57.1 Handling console requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261 57.2 Sending output to console . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263 vi
  • 9. 57.3 Are we in a console? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264 57.4 Reading values from console parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265 58 Console adapters 269 58.1 Retrieving console adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269 58.2 Using console adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270 59 Console prompts 59.1 Confirm . . 59.2 Line . . . . 59.3 Char . . . 59.4 Select . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273 274 274 275 276 60 Introduction to ZendCrypt 279 61 Encrypt/decrypt using block ciphers 281 62 Key derivation function 283 62.1 Pbkdf2 adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283 62.2 SaltedS2k adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284 62.3 Scrypt adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284 63 Password 287 63.1 Bcrypt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287 63.2 Apache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288 64 Public key cryptography 291 64.1 Diffie-Hellman . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291 64.2 RSA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293 65 ZendDbAdapter 65.1 Creating an Adapter - Quickstart . . . . . . . . . . . . . . . . . 65.2 Creating an Adapter Using Dependency Injection . . . . . . . . 65.3 Query Preparation Through ZendDbAdapterAdapter::query() 65.4 Query Execution Through ZendDbAdapterAdapter::query() . 65.5 Creating Statements . . . . . . . . . . . . . . . . . . . . . . . 65.6 Using the Driver Object . . . . . . . . . . . . . . . . . . . . . 65.7 Using The Platform Object . . . . . . . . . . . . . . . . . . . . 65.8 Using The Parameter Container . . . . . . . . . . . . . . . . . 65.9 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297 297 298 298 299 299 299 301 302 303 66 ZendDbResultSet 66.1 Quickstart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66.2 ZendDbResultSetResultSet and ZendDbResultSetAbstractResultSet . . . . . . . . . . . . . . . . 66.3 ZendDbResultSetHydratingResultSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305 305 306 306 67 ZendDbSql 67.1 ZendDbSqlSql (Quickstart) . . . . . . . . . . 67.2 ZendDbSql’s Select, Insert, Update and Delete 67.3 ZendDbSqlSelect . . . . . . . . . . . . . . . . 67.4 ZendDbSqlInsert . . . . . . . . . . . . . . . . 67.5 ZendDbSqlUpdate . . . . . . . . . . . . . . . 67.6 ZendDbSqlDelete . . . . . . . . . . . . . . . 67.7 ZendDbSqlWhere & ZendDbSqlHaving . . 309 309 310 310 313 314 314 314 68 ZendDbSqlDdl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321 vii
  • 10. 69 Creating Tables 323 70 Altering Tables 325 71 Dropping Tables 327 72 Executing DDL Statements 329 73 Currently Supported Data Types 331 74 Currently Supported Constraint Types 333 75 ZendDbTableGateway 335 75.1 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335 75.2 TableGateway Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337 76 ZendDbRowGateway 339 76.1 Quickstart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339 76.2 ActiveRecord Style Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340 77 ZendDbMetadata 341 77.1 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341 78 Introduction to ZendDi 345 78.1 Dependency Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345 78.2 Dependency Injection Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345 79 ZendDi Quickstart 80 ZendDi Definition 80.1 DefinitionList . . . 80.2 RuntimeDefinition 80.3 CompilerDefinition 80.4 ClassDefinition . . 347 . . . . 351 351 351 352 353 81 ZendDi InstanceManager 81.1 Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81.2 Preferences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81.3 Aliases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355 355 356 357 82 ZendDi Configuration 359 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 ZendDi Debugging & Complex Use Cases 361 83.1 Debugging a DiC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361 83.2 Complex Use Cases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361 84 Introduction to ZendDom 365 85 ZendDomQuery 367 85.1 Theory of Operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367 85.2 Methods Available . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368 86 Introduction to ZendEscaper 371 86.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371 86.2 What ZendEscaper is not . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372 viii
  • 11. 87 Theory of Operation 373 87.1 The Problem with Inconsistent Functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373 87.2 Why Contextual Escaping? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374 88 Configuring ZendEscaper 377 89 Escaping HTML 379 89.1 Examples of Bad HTML Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379 89.2 Examples of Good HTML Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 380 90 Escaping HTML Attributes 381 90.1 Examples of Bad HTML Attribute Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381 90.2 Examples of Good HTML Attribute Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382 91 Escaping Javascript 385 91.1 Examples of Bad Javascript Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385 91.2 Examples of Good Javascript Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386 92 Escaping Cascading Style Sheets 387 92.1 Examples of Bad CSS Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387 92.2 Examples of Good CSS Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387 93 Escaping URLs 389 93.1 Examples of Bad URL Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389 93.2 Examples of Good URL Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389 94 The EventManager 94.1 Overview . . . . . . . 94.2 Quick Start . . . . . . 94.3 Configuration Options 94.4 Available Methods . . 94.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391 391 391 394 395 396 95 Introduction to ZendFeed 401 95.1 Reading RSS Feed Data with ZendFeedReader . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401 96 Importing Feeds 403 96.1 Dumping the contents of a feed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403 97 Retrieving Feeds from Web Pages 405 97.1 Find Feed Links . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405 98 Consuming an RSS Feed 407 98.1 Reading a feed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407 98.2 Get properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407 99 Consuming an Atom Feed 409 99.1 Basic Use of an Atom Feed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 409 100Consuming a Single Atom Entry 411 100.1 Reading a Single-Entry Atom Feed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411 101ZendFeed and Security 413 101.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413 101.2 Filtering data using HTMLPurifier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413 101.3 Escaping data using ZendEscaper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 415 ix
  • 12. 102ZendFeedReaderReader 102.1 Introduction . . . . . . . . . . . . . . . . . . 102.2 Importing Feeds . . . . . . . . . . . . . . . . 102.3 Retrieving Underlying Feed and Entry Sources 102.4 Cache Support and Intelligent Requests . . . . 102.5 Locating Feed URIs from Websites . . . . . . 102.6 Attribute Collections . . . . . . . . . . . . . . 102.7 Retrieving Feed Information . . . . . . . . . . 102.8 Retrieving Entry/Item Information . . . . . . . 102.9 Extending Feed and Entry APIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417 417 417 418 419 420 421 421 424 426 103ZendFeedWriterWriter 103.1 Introduction . . . . . . . 103.2 Architecture . . . . . . . 103.3 Getting Started . . . . . . 103.4 Setting Feed Data Points . 103.5 Setting Entry Data Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431 431 431 432 434 436 104ZendFeedPubSubHubbub 104.1 What is PubSubHubbub? . . . . . . . . 104.2 Architecture . . . . . . . . . . . . . . 104.3 ZendFeedPubSubHubbubPublisher . 104.4 ZendFeedPubSubHubbubSubscriber . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 439 439 439 440 441 105ZendFileClassFileLocator 105.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105.2 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105.3 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447 447 447 447 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106Introduction to ZendFilter 449 106.1 What is a filter? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449 106.2 Basic usage of filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449 107Using the StaticFilter 451 107.1 Double filtering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451 108Standard Filter Classes 108.1 Alnum . . . . . . . . . . . 108.2 Alpha . . . . . . . . . . . . 108.3 BaseName . . . . . . . . . 108.4 Boolean . . . . . . . . . . . 108.5 Callback . . . . . . . . . . 108.6 Compress and Decompress . 108.7 Digits . . . . . . . . . . . . 108.8 Dir . . . . . . . . . . . . . 108.9 Encrypt and Decrypt . . . . 108.10HtmlEntities . . . . . . . . 108.11Int . . . . . . . . . . . . . . 108.12Null . . . . . . . . . . . . . 108.13NumberFormat . . . . . . . 108.14PregReplace . . . . . . . . 108.15RealPath . . . . . . . . . . 108.16StringToLower . . . . . . . 108.17StringToUpper . . . . . . . 108.18StringTrim . . . . . . . . . x . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 453 453 454 454 455 458 459 464 465 465 471 473 473 474 475 476 477 478 478
  • 13. 108.19StripNewLines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479 108.20StripTags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479 108.21UriNormalize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 481 109Word Filters 109.1 CamelCaseToDash . . . . 109.2 CamelCaseToSeparator . 109.3 CamelCaseToUnderscore 109.4 DashToCamelCase . . . . 109.5 DashToSeparator . . . . . 109.6 DashToUnderscore . . . . 109.7 SeparatorToCamelCase . 109.8 SeparatorToDash . . . . . 109.9 SeparatorToSeparator . . 109.10UnderscoreToCamelCase 109.11UnderscoreToSeparator . 109.12UnderscoreToDash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 483 483 483 484 484 485 485 486 486 487 488 488 489 110File Filter Classes 110.1 Decrypt . . . . 110.2 Encrypt . . . . 110.3 Lowercase . . 110.4 Rename . . . . 110.5 RenameUpload 110.6 Uppercase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491 491 491 491 491 493 494 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111Filter Chains 495 111.1 Setting Filter Chain Order . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495 111.2 Using the Plugin Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495 112ZendFilterInflector 112.1 Operation . . . . . . . . . . . . . . . . . . . . . . . . . . 112.2 Using Custom Filters . . . . . . . . . . . . . . . . . . . . 112.3 Setting the Inflector Target . . . . . . . . . . . . . . . . . 112.4 Inflection Rules . . . . . . . . . . . . . . . . . . . . . . . 112.5 Utility Methods . . . . . . . . . . . . . . . . . . . . . . . 112.6 Using a Traversable or an array with ZendFilterInflector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497 497 498 498 499 501 501 113Writing Filters 503 114Introduction to ZendForm 505 115Form Quick Start 115.1 Programmatic Form Creation . 115.2 Creation via Factory . . . . . . 115.3 Factory-backed Form Extension 115.4 Validating Forms . . . . . . . . 115.5 Hinting to the Input Filter . . . 115.6 Binding an object . . . . . . . . 115.7 Rendering . . . . . . . . . . . 115.8 Validation Groups . . . . . . . 115.9 Using Annotations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507 507 508 512 513 514 516 517 520 521 116Form Collections 525 116.1 Creating Fieldsets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 528 xi
  • 14. 116.2 116.3 116.4 116.5 116.6 The Form Element . . . . . . . . . . . . . . The Controller . . . . . . . . . . . . . . . . The View . . . . . . . . . . . . . . . . . . . Adding New Elements Dynamically . . . . . Validation groups for fieldsets and collection 117File Uploading 117.1 Standard Example . . . . . . 117.2 File Post-Redirect-Get Plugin 117.3 HTML5 Multi-File Uploads . 117.4 Upload Progress . . . . . . . 117.5 Additional Info . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 532 533 534 535 537 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 541 541 544 546 547 551 118Advanced use of forms 118.1 Short names . . . . . . . . . . 118.2 Creating custom elements . . . 118.3 Handling dependencies . . . . . 118.4 The specific case of initializers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 553 553 553 557 559 119Form Elements 119.1 Introduction . . . . 119.2 Element Base Class 119.3 Standard Elements . 119.4 HTML5 Elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 561 561 561 563 578 120Form View Helpers 120.1 Introduction . . . . . . . . . 120.2 Standard Helpers . . . . . . . 120.3 HTML5 Helpers . . . . . . . 120.4 File Upload Progress Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 593 593 593 605 609 . . . . . . . . . . . . . . . . 121Overview of ZendHttp 611 121.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 611 121.2 ZendHttp Request, Response and Headers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 611 122The Request Class 122.1 Overview . . . . . . . 122.2 Quick Start . . . . . . 122.3 Configuration Options 122.4 Available Methods . . 122.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 613 613 613 614 614 617 123The Response Class 123.1 Overview . . . . . . . 123.2 Quick Start . . . . . . 123.3 Configuration Options 123.4 Available Methods . . 123.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 619 619 619 620 620 623 124The Headers Class 124.1 Overview . . . . . . . . . . . . . . 124.2 Quick Start . . . . . . . . . . . . . 124.3 Configuration Options . . . . . . . 124.4 Available Methods . . . . . . . . . 124.5 ZendHttpHeader* Base Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625 625 625 626 626 628 xii
  • 15. 124.6 List of HTTP Header Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 628 124.7 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 629 125HTTP Client - Overview 125.1 Overview . . . . . . . 125.2 Quick Start . . . . . . 125.3 Configuration Options 125.4 Available Methods . . 125.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 633 633 633 634 634 638 126HTTP Client - Connection Adapters 126.1 Overview . . . . . . . . . . . . . . . . 126.2 The Socket Adapter . . . . . . . . . . 126.3 The Proxy Adapter . . . . . . . . . . . 126.4 The cURL Adapter . . . . . . . . . . . 126.5 The Test Adapter . . . . . . . . . . . . 126.6 Creating your own connection adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 641 641 641 644 645 646 648 127HTTP Client - Advanced Usage 127.1 HTTP Redirections . . . . . . . . . . . . . . . . 127.2 Adding Cookies and Using Cookie Persistence . 127.3 Setting Custom Request Headers . . . . . . . . 127.4 File Uploads . . . . . . . . . . . . . . . . . . . 127.5 Sending Raw POST Data . . . . . . . . . . . . 127.6 HTTP Authentication . . . . . . . . . . . . . . 127.7 Sending Multiple Requests With the Same Client 127.8 Data Streaming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 651 651 651 653 654 654 655 655 656 128HTTP Client - Static Usage 128.1 Overview . . . . . . . 128.2 Quick Start . . . . . . 128.3 Configuration Options 128.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 659 659 659 659 660 129Translating 129.1 Adding translations . 129.2 Supported formats . 129.3 Setting a locale . . . 129.4 Translating messages 129.5 Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661 661 662 662 662 662 130I18n View Helpers 130.1 Introduction . . . . . . . 130.2 CurrencyFormat Helper . 130.3 DateFormat Helper . . . . 130.4 NumberFormat Helper . . 130.5 Plural Helper . . . . . . . 130.6 Translate Helper . . . . . 130.7 TranslatePlural Helper . . 130.8 Abstract Translator Helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663 663 663 665 666 667 668 669 670 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131I18n Filters 671 131.1 Alnum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 671 131.2 Alpha . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672 131.3 NumberFormat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672 xiii
  • 16. 131.4 NumberParse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 673 132I18n Validators 675 133Float 677 133.1 Supported options for ZendI18nValidatorFloat . . . . . . . . . . . . . . . . . . . . . . . . . . . . 677 133.2 Simple float validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 677 133.3 Localized float validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 677 134Int 679 134.1 Supported options for ZendI18nValidatorInt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 679 134.2 Simple integer validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 679 134.3 Localized integer validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 679 135Introduction to ZendInputFilter 681 136File Upload Input 685 137Introduction to ZendJson 687 138Basic Usage 689 138.1 Pretty-printing JSON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 689 139Advanced Usage of ZendJson 139.1 JSON Objects . . . . . . 139.2 Encoding PHP objects . . 139.3 Internal Encoder/Decoder 139.4 JSON Expressions . . . . . . . . 140XML to JSON conversion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 691 691 691 692 692 693 141ZendJsonServer - JSON-RPC server 695 141.1 Advanced Details . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 697 142Introduction to ZendLdap 703 142.1 Theory of operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 703 143API overview 707 143.1 Configuration / options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 707 143.2 API Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 708 144ZendLdapLdap 709 144.1 ZendLdapCollection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 710 145ZendLdapAttribute 711 146ZendLdapConverterConverter 713 147ZendLdapDn 715 148ZendLdapFilter 717 149ZendLdapNode 719 150ZendLdapNodeRootDse 721 150.1 OpenLDAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 723 150.2 ActiveDirectory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 723 xiv
  • 17. 150.3 eDirectory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 724 151ZendLdapNodeSchema 727 151.1 OpenLDAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 729 151.2 ActiveDirectory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 730 152ZendLdapLdifEncoder 731 153Usage Scenarios 733 153.1 Authentication scenarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 733 153.2 Basic CRUD operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 733 153.3 Extended operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 735 154Tools 154.1 Creation and modification of DN strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154.2 Using the filter API to create search filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154.3 Modify LDAP entries using the Attribute API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 737 737 737 737 155Object-oriented access to the LDAP tree using ZendLdapNode 155.1 Basic CRUD operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155.2 Extended operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155.3 Tree traversal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 739 739 739 739 156Getting information from the LDAP server 741 156.1 RootDSE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 741 156.2 Schema Browsing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 741 157Serializing LDAP data to and from LDIF 743 157.1 Serialize a LDAP entry to LDIF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 743 157.2 Deserialize a LDIF string into a LDAP entry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 744 158The AutoloaderFactory 158.1 Overview . . . . . . . 158.2 Quick Start . . . . . . 158.3 Configuration Options 158.4 Available Methods . . 158.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 747 747 747 748 748 748 159The StandardAutoloader 159.1 Overview . . . . . . . 159.2 Quick Start . . . . . . 159.3 Configuration Options 159.4 Available Methods . . 159.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 749 749 750 751 751 752 160The ClassMapAutoloader 160.1 Overview . . . . . . . 160.2 Quick Start . . . . . . 160.3 Configuration Options 160.4 Available Methods . . 160.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 753 753 753 754 754 755 161The ModuleAutoloader 161.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161.2 Quickstart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 757 757 757 757 xv
  • 18. 161.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 758 161.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 758 162The SplAutoloader Interface 162.1 Overview . . . . . . . . 162.2 Quick Start . . . . . . . 162.3 Configuration Options . 162.4 Available Methods . . . 162.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 759 759 759 760 760 761 163The PluginClassLoader 163.1 Overview . . . . . . . 163.2 Quick Start . . . . . . 163.3 Configuration Options 163.4 Available Methods . . 163.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 763 763 763 764 764 765 164The ShortNameLocator Interface 164.1 Overview . . . . . . . . . . . 164.2 Quick Start . . . . . . . . . . 164.3 Configuration Options . . . . 164.4 Available Methods . . . . . . 164.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 769 769 769 770 770 770 165The PluginClassLocator interface 165.1 Overview . . . . . . . . . . . 165.2 Quick Start . . . . . . . . . . 165.3 Configuration Options . . . . 165.4 Available Methods . . . . . . 165.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 771 771 771 771 771 772 . . . . . 166The Class Map Generator utility: bin/classmap_generator.php 773 166.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 773 166.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 773 166.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 773 167Overview of ZendLog 167.1 Creating a Log . . . . . . 167.2 Logging Messages . . . . 167.3 Destroying a Log . . . . . 167.4 Using Built-in Priorities . 167.5 Understanding Log Events 167.6 Log PHP Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 775 775 776 776 776 777 777 168Writers 168.1 Writing to Streams . . . 168.2 Writing to Databases . . 168.3 Writing to FirePHP . . . 168.4 Stubbing Out the Writer 168.5 Testing with the Mock . 168.6 Compositing Writers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 779 779 780 781 781 781 782 . . . . . . 169Filters 783 169.1 Available filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 783 xvi
  • 19. 170Formatters 170.1 Simple Formatting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170.2 Formatting to XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170.3 Formatting to FirePhp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 785 785 786 786 171Introduction to ZendMail 787 171.1 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 787 171.2 Configuring the default sendmail transport . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 788 172ZendMailMessage 172.1 Overview . . . . . . . 172.2 Quick Start . . . . . . 172.3 Configuration Options 172.4 Available Methods . . 172.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 789 789 789 791 791 794 173ZendMailTransport 173.1 Overview . . . . . . . 173.2 Quick Start . . . . . . 173.3 Configuration Options 173.4 Available Methods . . 173.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 795 795 795 796 797 797 174ZendMailTransportSmtpOptions 174.1 Overview . . . . . . . . . . . . 174.2 Quick Start . . . . . . . . . . . 174.3 Configuration Options . . . . . 174.4 Available Methods . . . . . . . 174.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 799 799 799 801 801 802 175ZendMailTransportFileOptions 175.1 Overview . . . . . . . . . . . 175.2 Quick Start . . . . . . . . . . 175.3 Configuration Options . . . . 175.4 Available Methods . . . . . . 175.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 803 803 803 803 804 804 . . . . . 176Introduction to ZendMath 805 176.1 Random number generator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 805 176.2 Big integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 806 177ZendMime 809 177.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 809 177.2 Static Methods and Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 809 177.3 Instantiating ZendMime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 810 178ZendMimeMessage 178.1 Introduction . . . . . . . . . . . . . . . . . . . . . . 178.2 Instantiation . . . . . . . . . . . . . . . . . . . . . . 178.3 Adding MIME Parts . . . . . . . . . . . . . . . . . . 178.4 Boundary handling . . . . . . . . . . . . . . . . . . . 178.5 Parsing a string to create a ZendMimeMessage object 178.6 Available methods . . . . . . . . . . . . . . . . . . . 179ZendMimePart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 811 811 811 811 811 812 812 813 xvii
  • 20. 179.1 179.2 179.3 179.4 Introduction . . . . . . . . . . . . . . . . . . . . Instantiation . . . . . . . . . . . . . . . . . . . . Methods for rendering the message part to a string Available methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 813 813 813 814 180Introduction to the Module System 815 180.1 The autoload_*.php Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 816 181The Module Manager 817 181.1 Module Manager Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 817 181.2 Module Manager Listeners . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 817 182The Module Class 182.1 A Minimal Module . . . . . . . 182.2 A Typical Module Class . . . . 182.3 The “loadModules.post” Event 182.4 The MVC “bootstrap” Event . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821 821 821 822 823 183The Module Autoloader 825 183.1 Module Autoloader Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 825 183.2 Non-Standard / Explicit Module Paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 826 183.3 Packaging Modules with Phar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 827 184Best Practices when Creating Modules 829 185Introduction to the MVC Layer 185.1 Basic Application Structure . . . . . 185.2 Basic Module Structure . . . . . . . 185.3 Bootstrapping an Application . . . . 185.4 Bootstrapping a Modular Application 185.5 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831 832 832 834 836 837 186Quick Start 186.1 Install the Zend Skeleton Application . 186.2 Create a New Module . . . . . . . . . 186.3 Update the Module Class . . . . . . . . 186.4 Create a Controller . . . . . . . . . . . 186.5 Create a View Script . . . . . . . . . . 186.6 Create a Route . . . . . . . . . . . . . 186.7 Tell the Application About our Module 186.8 Test it Out! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 839 839 840 840 841 842 842 844 845 187Default Services 187.1 Theory of Operation . . . . . . . . 187.2 ServiceManager . . . . . . . . . . 187.3 Abstract Factories . . . . . . . . . 187.4 Plugin Managers . . . . . . . . . . 187.5 ViewManager . . . . . . . . . . . . 187.6 Application Configuration Options 187.7 Default Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 847 847 847 852 854 855 856 857 . . . . . . . . . . . . . . 188Routing 861 188.1 Router Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 863 188.2 HTTP Route Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 863 188.3 HTTP Routing Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 869 xviii
  • 21. 188.4 Console Route Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 871 189The MvcEvent 189.1 Order of events . . . . . . . . . . . . . . . 189.2 MvcEvent::EVENT_BOOTSTRAP . . . . 189.3 MvcEvent::EVENT_ROUTE . . . . . . . 189.4 MvcEvent::EVENT_DISPATCH . . . . . 189.5 MvcEvent::EVENT_DISPATCH_ERROR 189.6 MvcEvent::EVENT_RENDER . . . . . . 189.7 MvcEvent::EVENT_RENDER_ERROR . 189.8 MvcEvent::EVENT_FINISH . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 873 874 874 875 876 877 879 879 880 190The SendResponseEvent 883 190.1 Listeners . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 883 190.2 Triggerers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 884 191Available Controllers 885 191.1 Common Interfaces Used With Controllers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 885 191.2 The AbstractActionController . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 887 191.3 The AbstractRestfulController . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 888 192Controller Plugins 192.1 AcceptableViewModelSelector Plugin 192.2 FlashMessenger Plugin . . . . . . . . 192.3 Forward Plugin . . . . . . . . . . . . 192.4 Identity Plugin . . . . . . . . . . . . 192.5 Layout Plugin . . . . . . . . . . . . 192.6 Params Plugin . . . . . . . . . . . . 192.7 Post/Redirect/Get Plugin . . . . . . . 192.8 File Post/Redirect/Get Plugin . . . . 192.9 Redirect Plugin . . . . . . . . . . . . 192.10Url Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 891 891 892 894 894 895 895 896 896 898 898 193Examples 899 193.1 Controllers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 899 193.2 Bootstrapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 900 194Introduction to ZendNavigation 903 194.1 Pages and Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 903 194.2 View Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 904 195Quick Start 905 196Pages 907 197Common page features 909 198ZendNavigationPageMvc 913 199ZendNavigationPageUri 917 200Creating custom page types 919 201Creating pages using the page factory 921 xix
  • 22. 202Containers 202.1 Creating containers 202.2 Adding pages . . . 202.3 Removing pages . 202.4 Finding pages . . . 202.5 Iterating containers 202.6 Other operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 923 923 929 930 931 933 934 203View Helpers 203.1 Introduction . . . . . . . . . . . . 203.2 Translation of labels and titles . . . 203.3 Integration with ACL . . . . . . . . 203.4 Navigation setup used in examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 937 937 938 939 939 204View Helper - Breadcrumbs 204.1 Introduction . . . . . . . . . . . . . 204.2 Basic usage . . . . . . . . . . . . . . 204.3 Specifying indentation . . . . . . . . 204.4 Customize output . . . . . . . . . . . 204.5 Rendering using a partial view script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 945 945 945 946 946 947 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205View Helper - Links 949 205.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 949 205.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 951 206View Helper - Menu 206.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206.3 Calling renderMenu() directly . . . . . . . . . . . . . . . . . . . . . . 206.4 Rendering the deepest active menu . . . . . . . . . . . . . . . . . . . 206.5 Rendering with maximum depth . . . . . . . . . . . . . . . . . . . . . 206.6 Rendering with minimum depth . . . . . . . . . . . . . . . . . . . . . 206.7 Rendering only the active branch . . . . . . . . . . . . . . . . . . . . 206.8 Rendering only the active branch with minimum depth . . . . . . . . . 206.9 Rendering only the active branch with maximum depth . . . . . . . . . 206.10Rendering only the active branch with maximum depth and no parents . 206.11Rendering a custom menu using a partial view script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 955 955 956 957 958 958 959 960 961 961 962 962 207View Helper - Sitemap 207.1 Introduction . . . . . . . . . . . . 207.2 Basic usage . . . . . . . . . . . . . 207.3 Rendering using no ACL role . . . 207.4 Rendering using a maximum depth . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 965 965 966 967 968 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208View Helper - Navigation Proxy 971 208.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 971 208.2 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 971 209Introduction to ZendPaginator 973 210Usage 210.1 Paginating data collections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210.2 The DbSelect adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210.3 Rendering pages with view scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 975 975 976 977 xx
  • 23. 211Configuration 983 212Advanced usage 212.1 Custom data source adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212.2 Custom scrolling styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212.3 Caching features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 985 985 985 986 213Introduction to ZendPermissionsAcl 213.1 Resources . . . . . . . . . . . . . 213.2 Roles . . . . . . . . . . . . . . . 213.3 Creating the Access Control List . 213.4 Registering Roles . . . . . . . . . 213.5 Defining Access Controls . . . . 213.6 Querying an ACL . . . . . . . . 989 989 990 991 991 992 993 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214Refining Access Controls 995 214.1 Precise Access Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 995 214.2 Removing Access Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 997 215Advanced Usage 999 215.1 Storing ACL Data for Persistence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 999 215.2 Writing Conditional ACL Rules with Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 999 216Introduction to ZendPermissionsRbac 1001 216.1 Roles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1001 216.2 Permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1001 216.3 Dynamic Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1001 217Methods 1003 218Examples 218.1 Roles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218.2 Permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218.3 Dynamic Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1005 . 1005 . 1006 . 1006 219Progress Bars 219.1 Introduction . . . 219.2 Basic Usage . . . 219.3 Persistent Progress 219.4 Standard Adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220File Upload Handlers 220.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220.2 Methods of Reporting Progress . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220.3 Standard Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1009 1009 1009 1009 1010 1013 . 1013 . 1013 . 1014 221Introduction to ZendSerializer 1017 221.1 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1017 221.2 Basic configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1018 221.3 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1018 222ZendSerializerAdapter 222.1 The PhpSerialize Adapter 222.2 The IgBinary Adapter . . 222.3 The Wddx Adapter . . . . 222.4 The Json Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1021 1021 1021 1021 1022 xxi
  • 24. 222.5 The PythonPickle Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1022 222.6 The PhpCode Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1023 223Introduction to ZendServer 1025 224ZendServerReflection 1027 224.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1027 224.2 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1027 225ZendServiceManager 1029 226ZendServiceManager Quick Start 1033 226.1 Using Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1033 226.2 Modules as Service Providers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1034 226.3 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1034 227Delegator service factories 1039 227.1 Delegator factory signature . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1039 227.2 A Delegator factory use case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1039 228Lazy Services 228.1 Use cases . . . . . 228.2 Setup . . . . . . . 228.3 Practical example . 228.4 Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1043 1043 1043 1043 1045 229Session Config 1047 229.1 Standard Config . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1047 229.2 Session Config . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1048 229.3 Custom Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1049 230Session Container 1051 230.1 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1051 230.2 Setting the Default Session Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1051 231Session Manager 1053 231.1 Initializing the Session Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1053 231.2 Session Compatibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1055 232Session Save Handlers 232.1 Cache . . . . . . . . . 232.2 DbTableGateway . . . 232.3 MongoDB . . . . . . 232.4 Custom Save Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1057 1057 1057 1058 1059 233Session Storage 233.1 Array Storage . . . . . 233.2 Session Storage . . . . 233.3 Session Array Storage 233.4 Custom Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1061 1061 1061 1062 1062 234Session Validators 1063 234.1 Http User Agent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1063 234.2 Remote Addr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1063 234.3 Custom Validators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1064 xxii
  • 25. 235ZendSoapServer 235.1 ZendSoapServer constructor . . . . . 235.2 Methods to define Web Service API . . 235.3 Request and response objects handling 235.4 Document/Literal WSDL Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1065 1065 1066 1067 1069 236ZendSoapClient 1071 236.1 ZendSoapClient Constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1071 236.2 Performing SOAP Requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1072 237WSDL Accessor 237.1 ZendSoapWsdl constructor . . 237.2 addMessage() method . . . . . 237.3 addPortType() method . . . . . 237.4 addPortOperation() method . . 237.5 addBinding() method . . . . . . 237.6 addBindingOperation() method 237.7 addSoapBinding() method . . . 237.8 addSoapOperation() method . . 237.9 addService() method . . . . . . 237.10Type mapping . . . . . . . . . 237.11addDocumentation() method . . 237.12Get finalized WSDL document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1075 1075 1075 1076 1076 1076 1077 1077 1077 1077 1078 1079 1080 238AutoDiscovery 238.1 AutoDiscovery Introduction 238.2 Class autodiscovering . . . 238.3 Functions autodiscovering . 238.4 Autodiscovering Datatypes . 238.5 WSDL Binding Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1081 1081 1082 1083 1083 1083 . . . . . . . . . . 239ZendStdlibHydrator 239.1 HydratorInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239.2 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239.3 Available Implementations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1085 . 1085 . 1085 . 1086 240ZendStdlibHydratorFilter 240.1 Filter implementations . . . . . . . . 240.2 Remove filters . . . . . . . . . . . . 240.3 Add filters . . . . . . . . . . . . . . 240.4 Use the composite for complex filters 240.5 Using the provider interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1087 1087 1088 1088 1089 1090 241ZendStdlibHydratorStrategy 1093 241.1 Adding strategies to the hydrators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1093 241.2 Available implementations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1094 241.3 Writing custom strategies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1095 242ZendStdlibHydratorAggregateAggregateHydrator 1097 242.1 Installation requirements for the AggregateHydrator . . . . . . . . . . . . . . . . . . . . . . . . . . 1097 242.2 Example of AggregateHydrator usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1097 242.3 Advanced use cases of the AggregateHydrator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1098 243Introduction to ZendTag 1101 xxiii
  • 26. 244Creating tag clouds with ZendTagCloud 1103 244.1 Decorators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1105 245Introduction to ZendTest 1109 246Unit testing with PHPUnit 1111 247Setup your TestCase 1113 248Testing your Controllers and MVC Applications 1115 249Assertions 1117 250Request Assertions 1119 251CSS Selector Assertions 1121 252XPath Assertions 1123 253Redirect Assertions 1125 254Response Header Assertions 1127 255ZendTextFiglet 1129 255.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1129 255.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1130 256ZendTextTable 1131 256.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1131 256.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1132 257ZendUri 257.1 Overview . . . . . . . . . . . 257.2 Creating a New URI . . . . . 257.3 Manipulating an Existing URI 257.4 Common Instance Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1133 1133 1133 1134 1134 258Introduction to ZendValidator 258.1 What is a validator? . . . 258.2 Basic usage of validators . 258.3 Customizing messages . . 258.4 Translating messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1139 1139 1139 1140 1141 . . . . . . . . 259Standard Validation Classes 1143 260Alnum 260.1 Supported options for ZendI18nValidatorAlnum 260.2 Basic usage . . . . . . . . . . . . . . . . . . . . 260.3 Using whitespaces . . . . . . . . . . . . . . . . 260.4 Using different languages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1145 1145 1145 1145 1146 261Alpha 261.1 Supported options for ZendValidatorAlpha 261.2 Basic usage . . . . . . . . . . . . . . . . . . 261.3 Using whitespaces . . . . . . . . . . . . . . 261.4 Using different languages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1147 1147 1147 1147 1148 xxiv . . . . . . . .
  • 27. 262Barcode 262.1 Supported options for ZendValidatorBarcode 262.2 Basic usage . . . . . . . . . . . . . . . . . . . 262.3 Optional checksum . . . . . . . . . . . . . . . 262.4 Writing custom adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1149 1151 1152 1152 1152 263Between 1155 263.1 Supported options for ZendValidatorBetween . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1155 263.2 Default behaviour for ZendValidatorBetween . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1155 263.3 Validation exclusive the border values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1155 264Callback 264.1 Supported options for ZendValidatorCallback 264.2 Basic usage . . . . . . . . . . . . . . . . . . . 264.3 Usage with closures . . . . . . . . . . . . . . 264.4 Usage with class-based callbacks . . . . . . . 264.5 Adding options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1157 1157 1157 1157 1158 1159 265CreditCard 265.1 Supported options for ZendValidatorCreditCard 265.2 Basic usage . . . . . . . . . . . . . . . . . . . . 265.3 Accepting defined credit cards . . . . . . . . . . 265.4 Validation by using foreign APIs . . . . . . . . 265.5 Ccnum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1161 1162 1162 1162 1163 1164 266Date 266.1 Supported options for ZendValidatorDate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266.2 Default date validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266.3 Self defined date validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1165 . 1165 . 1165 . 1165 267DbRecordExists and DbNoRecordExists 267.1 Supported options for ZendValidatorDb* 267.2 Basic usage . . . . . . . . . . . . . . . . . 267.3 Excluding records . . . . . . . . . . . . . 267.4 Database Schemas . . . . . . . . . . . . . 267.5 Using a Select object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1167 1167 1167 1168 1169 1170 268Digits 1171 268.1 Supported options for ZendValidatorDigits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1171 268.2 Validating digits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1171 269EmailAddress 269.1 Basic usage . . . . . . . . . . . . . . . . . . . . 269.2 Options for validating Email Addresses . . . . . 269.3 Complex local parts . . . . . . . . . . . . . . . 269.4 Validating only the local part . . . . . . . . . . . 269.5 Validating different types of hostnames . . . . . 269.6 Checking if the hostname actually accepts email 269.7 Validating International Domains Names . . . . 269.8 Validating Top Level Domains . . . . . . . . . . 269.9 Setting messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1173 1173 1173 1174 1174 1174 1174 1175 1176 1176 270File Validation Classes 1177 270.1 Crc32 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1177 270.2 ExcludeExtension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1178 xxv
  • 28. 270.3 ExcludeMimeType 270.4 Exists . . . . . . . 270.5 Extension . . . . . 270.6 Hash . . . . . . . 270.7 ImageSize . . . . 270.8 IsCompressed . . 270.9 IsImage . . . . . . 270.10Md5 . . . . . . . . 270.11MimeType . . . . 270.12NotExists . . . . . 270.13Sha1 . . . . . . . 270.14Size . . . . . . . . 270.15UploadFile . . . . 270.16WordCount . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271Float 271.1 Supported options for ZendI18nValidatorFloat . . . . . . . . . . . . . . . . . . . . . . . . . . . 271.2 Simple float validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271.3 Localized float validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1178 1178 1179 1180 1180 1181 1182 1182 1183 1184 1185 1186 1186 1187 1189 . 1189 . 1189 . 1189 272GreaterThan 1191 272.1 Supported options for ZendValidatorGreaterThan . . . . . . . . . . . . . . . . . . . . . . . . . . . 1191 272.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1191 272.3 Validation inclusive the border value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1191 273Hex 273.1 Supported options for ZendValidatorHex 1193 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1193 274Hostname 274.1 Supported options for ZendValidatorHostname 274.2 Basic usage . . . . . . . . . . . . . . . . . . . . 274.3 Validating different types of hostnames . . . . . 274.4 Validating International Domains Names . . . . 274.5 Validating Top Level Domains . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1195 1195 1195 1196 1196 1197 275Iban 1199 275.1 Supported options for ZendValidatorIban . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1199 275.2 IBAN validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1199 276Identical 276.1 Supported options for ZendValidatorIdentical 276.2 Basic usage . . . . . . . . . . . . . . . . . . . 276.3 Identical objects . . . . . . . . . . . . . . . . 276.4 Form elements . . . . . . . . . . . . . . . . . 276.5 Strict validation . . . . . . . . . . . . . . . . . 276.6 Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1201 1201 1201 1201 1202 1204 1204 277InArray 277.1 Supported options for ZendValidatorInArray 277.2 Simple array validation . . . . . . . . . . . . . 277.3 Array validation modes . . . . . . . . . . . . 277.4 Recursive array validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1207 1207 1208 1208 1209 278Ip 1211 278.1 Supported options for ZendValidatorIp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1211 xxvi
  • 29. 278.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1211 278.3 Validate IPv4 or IPV6 alone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1212 279Isbn 279.1 279.2 279.3 279.4 Supported options for ZendValidatorIsbn Basic usage . . . . . . . . . . . . . . . . . Setting an explicit ISBN validation type . . Specifying a separator restriction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280LessThan 280.1 Supported options for ZendValidatorLessThan . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280.3 Validation inclusive the border value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1213 1213 1213 1213 1214 1215 . 1215 . 1215 . 1215 281NotEmpty 1217 281.1 Supported options for ZendValidatorNotEmpty . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1217 281.2 Default behaviour for ZendValidatorNotEmpty . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1217 281.3 Changing behaviour for ZendValidatorNotEmpty . . . . . . . . . . . . . . . . . . . . . . . . . . . 1217 282PostCode 1219 282.1 Constructor options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1220 282.2 Supported options for ZendValidatorPostCode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1220 283Regex 1221 283.1 Supported options for ZendValidatorRegex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1221 283.2 Validation with ZendValidatorRegex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1221 283.3 Pattern handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1221 284Sitemap Validators 284.1 SitemapChangefreq . . . . . . . . . . . . . . . 284.2 SitemapLastmod . . . . . . . . . . . . . . . . . 284.3 SitemapLoc . . . . . . . . . . . . . . . . . . . 284.4 SitemapPriority . . . . . . . . . . . . . . . . . 284.5 Supported options for ZendValidatorSitemap_* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1223 1223 1223 1224 1224 1224 285Step 285.1 Supported options for ZendValidatorStep . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285.3 Using floating-point values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1225 . 1225 . 1225 . 1225 286StringLength 286.1 Supported options for ZendValidatorStringLength 286.2 Default behaviour for ZendValidatorStringLength 286.3 Limiting the maximum allowed length of a string . 286.4 Limiting the minimal required length of a string . 286.5 Limiting a string on both sides . . . . . . . . . . . 286.6 Encoding of values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1227 1227 1227 1227 1228 1228 1228 287File Validation Classes 287.1 Crc32 . . . . . . . 287.2 ExcludeExtension 287.3 ExcludeMimeType 287.4 Exists . . . . . . . 287.5 Extension . . . . . 287.6 Hash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1231 1231 1232 1232 1232 1233 1234 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxvii
  • 30. 287.7 ImageSize . . 287.8 IsCompressed 287.9 IsImage . . . . 287.10Md5 . . . . . . 287.11MimeType . . 287.12NotExists . . . 287.13Sha1 . . . . . 287.14Size . . . . . . 287.15UploadFile . . 287.16WordCount . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1234 1235 1236 1236 1237 1238 1239 1240 1240 1241 288Validator Chains 1243 289Writing Validators 1245 290Validation Messages 1249 290.1 Using pre-translated validation messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1249 290.2 Limit the size of a validation message . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1250 291Getting the Zend Framework Version 1251 292ZendView Quick Start 1253 292.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1253 292.2 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1253 293The PhpRenderer 293.1 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293.2 Options and Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293.3 Additional Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1267 . 1267 . 1271 . 1271 294PhpRenderer View Scripts 1273 294.1 Escaping Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1274 295The ViewEvent 295.1 Order of events . . . . . . . . . . . . . . . 295.2 ViewEvent::EVENT_RENDERER . . . . 295.3 ViewEvent::EVENT_RENDERER_POST 295.4 ViewEvent::EVENT_RESPONSE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1275 1275 1276 1277 1277 296View Helpers 1279 296.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1279 296.2 Included Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1280 297View Helper - BasePath 1281 297.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1281 297.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1281 298View Helper - Cycle 1283 298.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1283 298.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1283 298.3 Working with two or more cycles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1284 299View Helper - Doctype 1285 299.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1285 299.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1286 299.3 Retrieving the Doctype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1286 xxviii
  • 31. 299.4 Choosing a Doctype to Use with the Open Graph Protocol . . . . . . . . . . . . . . . . . . . . . . . 1286 299.5 Zend MVC View Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1287 300FlashMessenger Helper 300.1 Introduction . . . . . . . . . . . . . . . . . 300.2 Basic Usage . . . . . . . . . . . . . . . . . 300.3 CSS Layout . . . . . . . . . . . . . . . . . . 300.4 HTML Layout . . . . . . . . . . . . . . . . 300.5 Sample Modification for Twitter Bootstrap 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1289 1289 1289 1289 1290 1290 301View Helper - HeadLink 1293 301.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1293 301.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1294 302View Helper - HeadMeta 302.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302.3 Usage with XHTML1_RDFA doctype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1295 . 1295 . 1296 . 1296 303View Helper - HeadScript 303.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303.3 Capturing Scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1299 . 1299 . 1301 . 1301 304View Helper - HeadStyle 1303 304.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1303 304.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1306 304.3 Capturing Style Declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1306 305View Helper - HeadTitle 1309 305.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1309 305.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1309 306View Helper - HtmlList 1311 306.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1311 306.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1311 307View Helper - HTML Object 1315 307.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1315 307.2 Flash helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1315 307.3 Customizing the object by passing additional arguments . . . . . . . . . . . . . . . . . . . . . . . . 1316 308View Helper - Identity 1317 308.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1317 308.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1317 308.3 Using with ServiceManager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1317 309View Helper - InlineScript 1319 309.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1319 309.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1319 309.3 Capturing Scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1320 310View Helper - JSON 1321 310.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1321 310.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1321 xxix
  • 32. 311View Helper - Partial 1323 311.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1323 311.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1323 311.3 Using PartialLoop to Render Iterable Models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1324 312View Helper - Placeholder 312.1 Introduction . . . . . . . 312.2 Basic Usage . . . . . . . 312.3 Aggregate Content . . . . 312.4 Capture Content . . . . . 312.5 Concrete Implementations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1327 1327 1327 1327 1328 1329 313View Helper - URL 1331 313.1 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1331 314Advanced usage of helpers 1333 314.1 Registering Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1333 314.2 Writing Custom Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1334 314.3 Registering Concrete Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1336 315Introduction to ZendXmlRpc 1337 315.1 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1337 316ZendXmlRpcClient 316.1 Introduction . . . . . . . 316.2 Method Calls . . . . . . . 316.3 Types and Conversions . . 316.4 Server Proxy Object . . . 316.5 Error Handling . . . . . . 316.6 Server Introspection . . . 316.7 From Request to Response 316.8 HTTP Client and Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1339 1339 1339 1340 1342 1342 1343 1344 1344 317ZendXmlRpcServer 317.1 Introduction . . . . . . . . . . . . . . . . . . 317.2 Basic Usage . . . . . . . . . . . . . . . . . . 317.3 Server Structure . . . . . . . . . . . . . . . . 317.4 Anatomy of a webservice . . . . . . . . . . . 317.5 Conventions . . . . . . . . . . . . . . . . . . 317.6 Utilizing Namespaces . . . . . . . . . . . . . 317.7 Custom Request Objects . . . . . . . . . . . . 317.8 Custom Responses . . . . . . . . . . . . . . . 317.9 Handling Exceptions via Faults . . . . . . . . 317.10Caching Server Definitions Between Requests 317.11Usage Examples . . . . . . . . . . . . . . . . 317.12Performance optimization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1347 1347 1347 1347 1348 1348 1349 1349 1350 1350 1350 1351 1355 318ZendServiceAkismet 318.1 Introduction . . . . . . . . . . 318.2 Verify an API key . . . . . . . 318.3 Check for spam . . . . . . . . . 318.4 Submitting known spam . . . . 318.5 Submitting false positives (ham) 318.6 Zend-specific Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1357 1357 1357 1358 1358 1359 1359 xxx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  • 33. 319ZendServiceAmazon 319.1 Introduction . . . . . . . . . . . . . . . . . . 319.2 Country Codes . . . . . . . . . . . . . . . . . 319.3 Looking up a Specific Amazon Item by ASIN . 319.4 Performing Amazon Item Searches . . . . . . 319.5 Using the Alternative Query API . . . . . . . 319.6 ZendServiceAmazon Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1361 1361 1362 1362 1363 1363 1364 320ZendServiceAppleApns 320.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320.3 Feedback Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1369 . 1369 . 1369 . 1371 321ZendServiceAudioscrobbler 321.1 Introduction . . . . . . 321.2 Users . . . . . . . . . . 321.3 Artists . . . . . . . . . 321.4 Tracks . . . . . . . . . 321.5 Tags . . . . . . . . . . . 321.6 Groups . . . . . . . . . 321.7 Forums . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1373 1373 1373 1375 1376 1376 1376 1377 322ZendServiceDelicious 322.1 Introduction . . . . . . . . . . 322.2 Retrieving posts . . . . . . . . 322.3 ZendServiceDeliciousPostList 322.4 Editing posts . . . . . . . . . . 322.5 Deleting posts . . . . . . . . . 322.6 Adding new posts . . . . . . . 322.7 Tags . . . . . . . . . . . . . . . 322.8 Bundles . . . . . . . . . . . . . 322.9 Public data . . . . . . . . . . . 322.10HTTP client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1379 1379 1379 1380 1381 1382 1382 1382 1383 1383 1384 323ZendServiceDeveloperGardenDeveloperGarden 323.1 Introduction to DeveloperGarden . . . . . . 323.2 BaseUserService . . . . . . . . . . . . . . . 323.3 IP Location . . . . . . . . . . . . . . . . . . 323.4 Local Search . . . . . . . . . . . . . . . . . 323.5 Send SMS . . . . . . . . . . . . . . . . . . 323.6 SMS Validation . . . . . . . . . . . . . . . . 323.7 Voice Call . . . . . . . . . . . . . . . . . . 323.8 ConferenceCall . . . . . . . . . . . . . . . . 323.9 Performance and Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1385 1385 1386 1387 1388 1388 1389 1389 1390 1392 324ZendServiceFlickr 324.1 Introduction . . . . . . . . . . . . . . . . . 324.2 Finding Flickr Users’ Photos and Information 324.3 Finding photos From a Group Pool . . . . . 324.4 Retrieving Flickr Image Details . . . . . . . 324.5 ZendServiceFlickr Result Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1393 1393 1393 1394 1394 1395 . . . . . . . . . . . . . . . . . . . . . 325ZendServiceGoogleGcm 1397 325.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1397 325.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1397 xxxi
  • 34. 326ZendServiceLiveDocxLiveDocx 1399 326.1 Introduction to LiveDocx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1399 326.2 ZendServiceLiveDocxMailMerge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1401 327ZendServiceNirvanix 327.1 Introduction . . . . . . . 327.2 Registering with Nirvanix 327.3 API Documentation . . . 327.4 Features . . . . . . . . . . 327.5 Getting Started . . . . . . 327.6 Understanding the Proxy . 327.7 Examining Results . . . . 327.8 Handling Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1419 1419 1419 1419 1419 1420 1420 1421 1422 328ZendServiceRackspace 328.1 Introduction . . . . . . . . 328.2 Registering with Rackspace 328.3 Cloud Files . . . . . . . . . 328.4 Cloud Servers . . . . . . . 328.5 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1423 1423 1423 1423 1424 1424 329ZendServiceReCaptcha 1427 329.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1427 329.2 Simplest use . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1427 329.3 Hiding email addresses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1428 330ZendServiceSlideShare 330.1 Getting Started with ZendServiceSlideShare 330.2 The SlideShow object . . . . . . . . . . . . 330.3 Retrieving a single slide show . . . . . . . . 330.4 Retrieving Groups of Slide Shows . . . . . . 330.5 ZendServiceSlideShare Caching policies . . 330.6 Changing the behavior of the HTTP Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1431 1431 1431 1434 1434 1435 1435 331ZendServiceStrikeIron 331.1 Overview . . . . . . . . . . 331.2 Registering with StrikeIron 331.3 Getting Started . . . . . . . 331.4 Making Your First Query . . 331.5 Examining Results . . . . . 331.6 Handling Errors . . . . . . 331.7 Checking Your Subscription . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1437 1437 1438 1438 1438 1439 1440 1440 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332ZendServiceStrikeIron: Bundled Services 1443 332.1 ZIP Code Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1443 332.2 U.S. Address Verification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1444 332.3 Sales & Use Tax Basic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1445 333ZendServiceStrikeIron: Advanced Uses 1447 333.1 Using Services by WSDL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1447 333.2 Viewing SOAP Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1448 334ZendServiceTechnorati 1449 334.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1449 334.2 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1449 xxxii
  • 35. 334.3 334.4 334.5 334.6 334.7 334.8 Making Your First Query . . . . . . . Consuming Results . . . . . . . . . . Handling Errors . . . . . . . . . . . Checking Your API Key Daily Usage Available Technorati Queries . . . . ZendServiceTechnorati Classes . . . 335ZendServiceTwitter 335.1 Introduction . . . . . . 335.2 Quick Start . . . . . . . 335.3 Authentication . . . . . 335.4 Account Methods . . . . 335.5 Application Methods . . 335.6 Blocking Methods . . . 335.7 Direct Message Methods 335.8 Favorites Methods . . . 335.9 Friendship Methods . . 335.10Search Methods . . . . 335.11Status Methods . . . . . 335.12User Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1449 1450 1452 1452 1453 1456 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1461 1461 1461 1463 1464 1465 1465 1466 1467 1467 1468 1468 1470 336ZendServiceWindowsAzure 336.1 Introduction . . . . . . . . . . . . 336.2 Installing the Windows Azure SDK 336.3 API Documentation . . . . . . . . 336.4 Features . . . . . . . . . . . . . . . 336.5 Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1473 1473 1473 1473 1473 1474 337ZendServiceWindowsAzureStorageBlob 337.1 API Examples . . . . . . . . . . . . . 337.2 Root container . . . . . . . . . . . . . 337.3 Blob storage stream wrapper . . . . . . 337.4 Shared Access Signature . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1475 1475 1477 1477 1478 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338ZendServiceWindowsAzureStorageTable 1481 338.1 Operations on tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1481 338.2 Operations on entities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1482 338.3 Table storage session handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1488 339ZendServiceWindowsAzureStorageQueue 1491 339.1 API Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1491 340Copyright Information 1495 341Introduction to Zend Framework 2 1497 342User Guide 1499 343Zend Framework Tool (ZFTool) 1501 344Learning Zend Framework 2 1503 345Migration 1505 346Zend Framework 2 Reference 1507 346.1 ZendAuthentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1507 xxxiii
  • 36. 346.2 ZendBarcode . . . . . 346.3 ZendCache . . . . . . . 346.4 ZendCaptcha . . . . . . 346.5 ZendConfig . . . . . . 346.6 ZendConsole . . . . . . 346.7 ZendCrypt . . . . . . . 346.8 ZendDb . . . . . . . . 346.9 ZendDi . . . . . . . . . 346.10ZendDom . . . . . . . 346.11ZendEscaper . . . . . . 346.12ZendEventManager . . 346.13ZendFeed . . . . . . . 346.14ZendFile . . . . . . . . 346.15ZendFilter . . . . . . . 346.16ZendForm . . . . . . . 346.17ZendHttp . . . . . . . . 346.18ZendI18n . . . . . . . 346.19ZendInputFilter . . . . 346.20ZendJson . . . . . . . . 346.21ZendLdap . . . . . . . 346.22ZendLoader . . . . . . 346.23ZendLog . . . . . . . . 346.24ZendMail . . . . . . . 346.25ZendMath . . . . . . . 346.26ZendMime . . . . . . . 346.27ZendModuleManager . 346.28ZendMvc . . . . . . . 346.29ZendNavigation . . . . 346.30ZendPaginator . . . . . 346.31ZendPermissionsAcl . 346.32ZendPermissionsRbac 346.33ZendProgressBar . . . 346.34ZendSerializer . . . . . 346.35ZendServer . . . . . . 346.36ZendServiceManager . 346.37ZendSession . . . . . . 346.38ZendSoap . . . . . . . 346.39ZendStdlib . . . . . . . 346.40ZendTag . . . . . . . . 346.41ZendTest . . . . . . . . 346.42ZendText . . . . . . . . 346.43ZendUri . . . . . . . . 346.44ZendValidator . . . . . 346.45ZendVersion . . . . . . 346.46ZendView . . . . . . . 346.47ZendXmlRpc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1507 1507 1508 1508 1508 1508 1509 1509 1509 1509 1510 1510 1510 1510 1510 1511 1511 1511 1511 1512 1512 1512 1512 1513 1513 1513 1513 1513 1514 1514 1514 1514 1514 1515 1515 1515 1515 1515 1516 1516 1516 1516 1516 1516 1516 1517 347Services for Zend Framework 2 Reference 347.1 ZendServiceAkismet . . . . . . . . . 347.2 ZendServiceAmazon . . . . . . . . . 347.3 ZendServiceAppleApns . . . . . . . . 347.4 ZendServiceAudioscrobbler . . . . . . 347.5 ZendServiceDel.icio.us . . . . . . . . 347.6 ZendServiceDeveloper Garden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1519 1519 1519 1519 1519 1519 1519 xxxiv . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  • 37. 347.7 ZendServiceFlickr . . . . . . 347.8 ZendServiceGoogleGcm . . 347.9 ZendServiceLiveDocx . . . . 347.10ZendServiceNirvanix . . . . 347.11ZendServiceRackspace . . . 347.12ZendServiceReCaptcha . . . 347.13ZendServiceSlideShare . . . 347.14ZendServiceStrikeIron . . . . 347.15ZendServiceTechnorati . . . 347.16ZendServiceTwitter . . . . . 347.17ZendServiceWindows Azure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1520 1520 1520 1520 1520 1520 1520 1520 1520 1521 1521 348Copyright 1523 349Indices and tables 1525 xxxv
  • 38. xxxvi
  • 39. CHAPTER 1 Overview Zend Framework 2 is an open source framework for developing web applications and services using PHP 5.3+. Zend Framework 2 uses 100% object-oriented code and utilises most of the new features of PHP 5.3, namely namespaces, late static binding, lambda functions and closures. Zend Framework 2 evolved from Zend Framework 1, a successful PHP framework with over 15 million downloads. Note: ZF2 is not backward compatible with ZF1, because of the new features in PHP 5.3+ implemented by the framework, and due to major rewrites of many components. The component structure of Zend Framework 2 is unique; each component is designed with few dependencies on other components. ZF2 follows the SOLID object-oriented design principle. This loosely coupled architecture allows developers to use whichever components they want. We call this a “use-at-will” design. We support Pyrus and Composer as installation and dependency tracking mechanisms for the framework as a whole and for each component, further enhancing this design. We use PHPUnit to test our code and Travis CI as a Continuous Integration service. While they can be used separately, Zend Framework 2 components in the standard library form a powerful and extensible web application framework when combined. Also, it offers a robust, high performance MVC implementation, a database abstraction that is simple to use, and a forms component that implements HTML5 form rendering, validation, and filtering so that developers can consolidate all of these operations using one easy-to-use, object oriented interface. Other components, such as ZendAuthentication and ZendPermissionsAcl, provide user authentication and authorization against all common credential stores. Still others, with the ZendService namespace, implement client libraries to simply access the most popular web services available. Whatever your application needs are, you’re likely to find a Zend Framework 2 component that can be used to dramatically reduce development time with a thoroughly tested foundation. The principal sponsor of the project ‘Zend Framework 2’ is Zend Technologies, but many companies have contributed components or significant features to the framework. Companies such as Google, Microsoft, and StrikeIron have partnered with Zend to provide interfaces to web services and other technologies they wish to make available to Zend Framework 2 developers. Zend Framework 2 could not deliver and support all of these features without the help of the vibrant Zend Framework 2 community. Community members, including contributors, make themselves available on mailing lists, IRC channels and other forums. Whatever question you have about Zend Framework 2, the community is always available to address it. 1
  • 40. Zend Framework 2 Documentation, Release 2.2.5 2 Chapter 1. Overview
  • 41. CHAPTER 2 Installation • New to Zend Framework? Download the latest stable release. Available in .zip and .tar.gz formats. • Brave, cutting edge? Download Zend Framework’s Git repository using a Git client. Zend Framework is open source software, and the Git repository used for its development is publicly available on GitHub. Consider using Git to get Zend Framework if you want to contribute back to the framework, or need to upgrade your framework version more often than releases occur. Once you have a copy of Zend Framework available, your application needs to be able to access the framework classes found in the library folder. There are several ways to achieve this. Failing to find a Zend Framework 2 installation, the following error occurs: Fatal error: Uncaught exception ’RuntimeException’ with message ’Unable to load ZF2. Run ‘php composer.phar install‘ or define a ZF2_PATH environment variable.’ To fix that, you can add the Zend Framework’s library path to the PHP include_path. Also, you should set an environment path named ‘ZF2_PATH’ in httpd.conf (or equivalent). i.e. SetEnv ZF2_PATH /var/ZF2 running Linux. Rob Allen has kindly provided the community with an introductory tutorial, Getting Started with Zend Framework 2. Other Zend Framework community members are actively working on expanding the tutorial. 3
  • 42. Zend Framework 2 Documentation, Release 2.2.5 4 Chapter 2. Installation
  • 43. CHAPTER 3 Getting Started with Zend Framework 2 This tutorial is intended to give an introduction to using Zend Framework 2 by creating a simple database driven application using the Model-View-Controller paradigm. By the end you will have a working ZF2 application and you can then poke around the code to find out more about how it all works and fits together. 3.1 Some assumptions This tutorial assumes that you are running at least PHP 5.3.3 with the Apache web server and MySQL, accessible via the PDO extension. Your Apache installation must have the mod_rewrite extension installed and configured. You must also ensure that Apache is configured to support .htaccess files. This is usually done by changing the setting: 1 AllowOverride None to 1 AllowOverride FileInfo in your httpd.conf file. Check with your distributions documentation for exact details. You will not be able to navigate to any page other than the home page in this tutorial if you have not configured mod_rewrite and .htaccess usage correctly. 3.2 The tutorial application The application that we are going to build is a simple inventory system to display which albums we own. The main page will list our collection and allow us to add, edit and delete CDs. We are going to need four pages in our website: Page List of albums Add new album Edit album Delete album Description This will display the list of albums and provide links to edit and delete them. Also, a link to enable adding new albums will be provided. This page will provide a form for adding a new album. This page will provide a form for editing an album. This page will confirm that we want to delete an album and then delete it. 5
  • 44. Zend Framework 2 Documentation, Release 2.2.5 We will also need to store our data into a database. We will only need one table with these fields in it: Field name id artist title 6 Type integer varchar(100) varchar(100) Null? No No No Notes Primary key, auto-increment Chapter 3. Getting Started with Zend Framework 2
  • 45. CHAPTER 4 Getting started: A skeleton application In order to build our application, we will start with the ZendSkeletonApplication available on github. Use Composer (http://getcomposer.org) to create a new project from scratch with Zend Framework: 1 2 php composer.phar create-project --repository-url="https://packages.zendframework.com" -s dev zendfr php composer.phar update Note: Another way to install the ZendSkeletonApplication is to use github. Go to https://github.com/zendframework/ZendSkeletonApplication and click the Zip button. This will download a file with a name like ZendSkeletonApplication-master.zip or similar. Unzip this file into the directory where you keep all your vhosts and rename the resultant directory to zf2-tutorial. ZendSkeletonApplication is set up to use Composer (http://getcomposer.org) to resolve its dependencies. In this case, the dependency is Zend Framework 2 itself. To install Zend Framework 2 into our application we simply type: 1 2 3 php composer.phar self-update php composer.phar install php composer.phar update from the zf2-tutorial folder. This takes a while. You should see an output like: 1 2 3 Installing dependencies from lock file - Installing zendframework/zendframework (dev-master) Cloning 18c8e223f070deb07c17543ed938b54542aa0ed8 4 5 Generating autoload files Note: If you see this message: 1 2 [RuntimeException] The process timed out. then your connection was too slow to download the entire package in time, and composer timed out. To avoid this, instead of running: 7
  • 46. Zend Framework 2 Documentation, Release 2.2.5 1 2 php composer.phar install php composer.phar update run instead: 1 2 COMPOSER_PROCESS_TIMEOUT=5000 php composer.phar install COMPOSER_PROCESS_TIMEOUT=5000 php composer.phar update We can now move on to the virtual host. 4.1 Using the Apache Web Server You now need to create an Apache virtual host for the application and edit your hosts file so that http://zf2tutorial.localhost will serve index.php from the zf2-tutorial/public directory. Setting up the virtual host is usually done within httpd.conf or extra/httpd-vhosts.conf. If you are using httpd-vhosts.conf, ensure that this file is included by your main httpd.conf file. Some Linux distributions (ex: Ubuntu) package Apache so that configuration files are stored in /etc/apache2 and create one file per virtual host inside folder /etc/apache2/sites-enabled. In this case, you would place the virtual host block below into the file /etc/apache2/sites-enabled/zf2-tutorial. Ensure that NameVirtualHost is defined and set to *:80 or similar, and then define a virtual host along these lines: 1 2 3 4 5 6 7 8 9 10 11 <VirtualHost *:80> ServerName zf2-tutorial.localhost DocumentRoot /path/to/zf2-tutorial/public SetEnv APPLICATION_ENV "development" <Directory /path/to/zf2-tutorial/public> DirectoryIndex index.php AllowOverride All Order allow,deny Allow from all </Directory> </VirtualHost> Make sure that you update your /etc/hosts or c:windowssystem32driversetchosts file so that zf2-tutorial.localhost is mapped to 127.0.0.1. The website can then be accessed using http://zf2tutorial.localhost. 127.0.0.1 zf2-tutorial.localhost localhost Restart your web server. If youve done it right, you should see something like this: 8 Chapter 4. Getting started: A skeleton application
  • 47. Zend Framework 2 Documentation, Release 2.2.5 To test that your .htaccess file is working, navigate to http://zf2-tutorial.localhost/1234 and you should see this: If you see a standard Apache 404 error, then you need to fix .htaccess usage before continuing. If you’re are using IIS with the URL Rewrite Module, import the following: 1 2 RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^.*$ index.php [NC,L] You now have a working skeleton application and we can start adding the specifics for our application. 4.2 Error reporting Optionally, you can use the APPLICATION_ENV setting in your virtualhost to let PHP output all its errors to the browser. This can be useful when during development of your application. Edit index.php from the zf2-tutorial/public/ directory and change it to the following: 4.2. Error reporting 9
  • 48. Zend Framework 2 Documentation, Release 2.2.5 1 <?php 2 3 4 5 6 7 8 9 /** * Display all errors when APPLICATION_ENV is development. */ if ($_SERVER[’APPLICATION_ENV’] == ’development’) { error_reporting(E_ALL); ini_set("display_errors", 1); } 10 11 12 13 14 15 /** * This makes our life easier when dealing with paths. Everything is relative * to the application root now. */ chdir(dirname(__DIR__)); 16 17 18 // Setup autoloading require ’init_autoloader.php’; 19 20 21 // Run the application! ZendMvcApplication::init(require ’config/application.config.php’)->run(); 10 Chapter 4. Getting started: A skeleton application
  • 49. CHAPTER 5 Modules Zend Framework 2 uses a module system and you organise your main application-specific code within each module. The Application module provided by the skeleton is used to provide bootstrapping, error and routing configuration to the whole application. It is usually used to provide application level controllers for, say, the home page of an application, but we are not going to use the default one provided in this tutorial as we want our album list to be the home page, which will live in our own module. We are going to put all our code into the Album module which will contain our controllers, models, forms and views, along with configuration. Well also tweak the Application module as required. Lets start with the directories required. 5.1 Setting up the Album module Start by creating a directory called Album under module with the following subdirectories to hold the modules files: 1 2 3 4 5 6 7 8 9 10 11 12 zf2-tutorial/ /module /Album /config /src /Album /Controller /Form /Model /view /album /album As you can see the Album module has separate directories for the different types of files we will have. The PHP files that contain classes within the Album namespace live in the src/Album directory so that we can have multiple namespaces within our module should we require it. The view directory also has a sub-folder called album for our modules view scripts. In order to load and configure a module, Zend Framework 2 has a ModuleManager. This will look for Module.php in the root of the module directory (module/Album) and expect to find a class called AlbumModule within it. That is, the classes within a given module will have the namespace of the modules name, which is the directory name of the module. 11
  • 50. Zend Framework 2 Documentation, Release 2.2.5 Create Module.php in the Album zf2-tutorial/module/Album: 1 module: Create a file called Module.php under namespace Album; 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Module { public function getAutoloaderConfig() { return array( ’ZendLoaderClassMapAutoloader’ => array( __DIR__ . ’/autoload_classmap.php’, ), ’ZendLoaderStandardAutoloader’ => array( ’namespaces’ => array( __NAMESPACE__ => __DIR__ . ’/src/’ . __NAMESPACE__, ), ), ); } 18 public function getConfig() { return include __DIR__ . ’/config/module.config.php’; } 19 20 21 22 23 } The ModuleManager will call getAutoloaderConfig() and getConfig() automatically for us. 5.1.1 Autoloading files Our getAutoloaderConfig() method returns an array that is compatible with ZF2s AutoloaderFactory. We configure it so that we add a class map file to the ClassMapAutoloader and also add this modules namespace to the StandardAutoloader. The standard autoloader requires a namespace and the path where to find the files for that namespace. It is PSR-0 compliant and so classes map directly to files as per the PSR-0 rules. As we are in development, we dont need to load files via the classmap, so we provide an empty array for the classmap autoloader. Create a file called autoload_classmap.php under zf2-tutorial/module/Album: 1 return array(); As this is an empty array, whenever the autoloader looks for a class within the Album namespace, it will fall back to the to StandardAutoloader for us. Note: If you are using Composer, you could instead just create an empty getAutoloaderConfig() { } and add to composer.json: 1 2 3 "autoload": { "psr-0": { "Album": "module/Album/src/" } }, If you go this way, then you need to run php composer.phar update to update the composer autoloading files. 12 Chapter 5. Modules
  • 51. Zend Framework 2 Documentation, Release 2.2.5 5.2 Configuration Having registered the autoloader, lets have a quick look at the getConfig() method in AlbumModule. This method simply loads the config/module.config.php file. Create a file called module.config.php under zf2-tutorial/module/Album/config: 1 2 3 4 5 6 7 8 9 10 11 12 return array( ’controllers’ => array( ’invokables’ => array( ’AlbumControllerAlbum’ => ’AlbumControllerAlbumController’, ), ), ’view_manager’ => array( ’template_path_stack’ => array( ’album’ => __DIR__ . ’/../view’, ), ), ); The config information is passed to the relevant components by the ServiceManager. We need two initial sections: controllers and view_manager. The controllers section provides a list of all the controllers provided by the module. We will need one controller, AlbumController, which well reference as AlbumControllerAlbum. The controller key must be unique across all modules, so we prefix it with our module name. Within the view_manager section, we add our view directory to the TemplatePathStack configuration. This will allow it to find the view scripts for the Album module that are stored in our view/ directory. 5.3 Informing the application about our new module We now need to tell the ModuleManager that this new module exists. This is done in the applications config/application.config.php file which is provided by the skeleton application. Update this file so that its modules section contains the Album module as well, so the file now looks like this: (Changes required are highlighted using comments.) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 return array( ’modules’ => array( ’Application’, ’Album’, // <-- Add this line ), ’module_listener_options’ => array( ’config_glob_paths’ => array( ’config/autoload/{,*.}{global,local}.php’, ), ’module_paths’ => array( ’./module’, ’./vendor’, ), ), ); As you can see, we have added our Album module into the list of modules after the Application module. We have now set up the module ready for putting our custom code into it. 5.2. Configuration 13
  • 52. Zend Framework 2 Documentation, Release 2.2.5 14 Chapter 5. Modules
  • 53. CHAPTER 6 Routing and controllers We will build a very simple inventory system to display our album collection. The home page will list our collection and allow us to add, edit and delete albums. Hence the following pages are required: Page Home Add new album Edit album Delete album Description This will display the list of albums and provide links to edit and delete them. Also, a link to enable adding new albums will be provided. This page will provide a form for adding a new album. This page will provide a form for editing an album. This page will confirm that we want to delete an album and then delete it. Before we set up our files, its important to understand how the framework expects the pages to be organised. Each page of the application is known as an action and actions are grouped into controllers within modules. Hence, you would generally group related actions into a controller; for instance, a news controller might have actions of current, archived and view. As we have four pages that all apply to albums, we will group them in a single controller AlbumController within our Album module as four actions. The four actions will be: Page Home Add new album Edit album Delete album Controller AlbumController AlbumController AlbumController AlbumController Action index add edit delete The mapping of a URL to a particular action is done using routes that are defined in the modules module.config.php file. We will add a route for our album actions. This is the updated module config file with the new code highlighted. 1 2 3 4 5 6 return array( ’controllers’ => array( ’invokables’ => array( ’AlbumControllerAlbum’ => ’AlbumControllerAlbumController’, ), ), 7 8 9 10 // The following section is new and should be added to your file ’router’ => array( ’routes’ => array( 15
  • 54. Zend Framework 2 Documentation, Release 2.2.5 ’album’ => array( ’type’ => ’segment’, ’options’ => array( ’route’ => ’/album[/][:action][/:id]’, ’constraints’ => array( ’action’ => ’[a-zA-Z][a-zA-Z0-9_-]*’, ’id’ => ’[0-9]+’, ), ’defaults’ => array( ’controller’ => ’AlbumControllerAlbum’, ’action’ => ’index’, ), ), ), 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ), 25 ), 26 27 ’view_manager’ => array( ’template_path_stack’ => array( ’album’ => __DIR__ . ’/../view’, ), ), 28 29 30 31 32 33 ); The name of the route is album and has a type of segment. The segment route allows us to specify placeholders in the URL pattern (route) that will be mapped to named parameters in the matched route. In this case, the route is ‘‘/album[/:action][/:id]‘‘ which will match any URL that starts with /album. The next segment will be an optional action name, and then finally the next segment will be mapped to an optional id. The square brackets indicate that a segment is optional. The constraints section allows us to ensure that the characters within a segment are as expected, so we have limited actions to starting with a letter and then subsequent characters only being alphanumeric, underscore or hyphen. We also limit the id to a number. This route allows us to have the following URLs: URL /album /album/add /album/edit/2 /album/delete/4 16 Page Home (list of albums) Add new album Edit album with an id of 2 Delete album with an id of 4 Action index add edit delete Chapter 6. Routing and controllers
  • 55. CHAPTER 7 Create the controller We are now ready to set up our controller. In Zend Framework 2, the controller is a class that is generally called {Controller name}Controller. Note that {Controller name} must start with a capital letter. This class lives in a file called {Controller name}Controller.php within the Controller directory for the module. In our case that is module/Album/src/Album/Controller. Each action is a public method within the controller class that is named {action name}Action. In this case {action name} should start with a lower case letter. Note: This is by convention. Zend Framework 2 doesnt provide many restrictions on controllers other than that they must implement the ZendStdlibDispatchable interface. The framework provides two abstract classes that do this for us: ZendMvcControllerAbstractActionController and ZendMvcControllerAbstractRestfulController. Well be using the standard AbstractActionController, but if youre intending to write a RESTful web service, AbstractRestfulController may be useful. Lets go ahead and create our controller class zf2-tutorials/module/Album/src/Album/Controller : 1 AlbumController.php at namespace AlbumController; 2 3 4 use ZendMvcControllerAbstractActionController; use ZendViewModelViewModel; 5 6 7 8 9 10 class AlbumController extends AbstractActionController { public function indexAction() { } 11 12 13 14 public function addAction() { } 15 16 17 18 public function editAction() { } 19 20 21 public function deleteAction() { 17
  • 56. Zend Framework 2 Documentation, Release 2.2.5 } 22 23 } Note: We have already informed the module about our controller in the controller section of module/Album/config/module.config.php. We have now set up the four actions that we want to use. They wont work yet until we set up the views. The URLs for each action are: URL http://zf2-tutorial.localhost/album http://zf2-tutorial.localhost/album/add http://zf2-tutorial.localhost/album/edit http://zf2-tutorial.localhost/album/delete Method called AlbumControllerAlbumController::indexAction AlbumControllerAlbumController::addAction AlbumControllerAlbumController::editAction AlbumControllerAlbumController::deleteAction We now have a working router and the actions are set up for each page of our application. Its time to build the view and the model layer. 7.1 Initialise the view scripts To integrate the view into our application all we need to do is create some view script files. These files will be executed by the DefaultViewStrategy and will be passed any variables or view models that are returned from the controller action method. These view scripts are stored in our modules views directory within a directory named after the controller. Create these four empty files now: • module/Album/view/album/album/index.phtml • module/Album/view/album/album/add.phtml • module/Album/view/album/album/edit.phtml • module/Album/view/album/album/delete.phtml We can now start filling everything in, starting with our database and models. 18 Chapter 7. Create the controller
  • 57. CHAPTER 8 Database and models 8.1 The database Now that we have the Album module set up with controller action methods and view scripts, it is time to look at the model section of our application. Remember that the model is the part that deals with the applications core purpose (the so-called business rules) and, in our case, deals with the database. We will make use of the Zend Framework class ZendDbTableGatewayTableGateway which is used to find, insert, update and delete rows from a database table. We are going to use MySQL, via PHPs PDO driver, so create a database called zf2tutorial, and run these SQL statements to create the album table with some data in it. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 CREATE TABLE album ( id int(11) NOT NULL auto_increment, artist varchar(100) NOT NULL, title varchar(100) NOT NULL, PRIMARY KEY (id) ); INSERT INTO album (artist, title) VALUES (’The Military Wives’, ’In My Dreams’); INSERT INTO album (artist, title) VALUES (’Adele’, ’21’); INSERT INTO album (artist, title) VALUES (’Bruce Springsteen’, ’Wrecking Ball (Deluxe)’); INSERT INTO album (artist, title) VALUES (’Lana Del Rey’, ’Born To Die’); INSERT INTO album (artist, title) VALUES (’Gotye’, ’Making Mirrors’); (The test data chosen happens to be the Bestsellers on Amazon UK at the time of writing!) We now have some data in a database and can write a very simple model for it. 8.2 The model files Zend Framework does not provide a ZendModel component because the model is your business logic and its up to you to decide how you want it to work. There are many components that you can use for this depending on your needs. One approach is to have model classes represent each entity in your application and then use mapper objects 19
  • 58. Zend Framework 2 Documentation, Release 2.2.5 that load and save entities to the database. Another is to use an Object-relational mapping (ORM) technology, such as Doctrine or Propel. For this tutorial, we are going to create a very simple model by creating an AlbumTable class that uses the ZendDbTableGatewayTableGateway class in which each album object is an Album object (known as an entity). This is an implementation of the Table Data Gateway design pattern to allow for interfacing with data in a database table. Be aware though that the Table Data Gateway pattern can become limiting in larger systems. There is also a temptation to put database access code into controller action methods as these are exposed by ZendDbTableGatewayAbstractTableGateway. Dont do this! Lets start by creating a file called Album.php under module/Album/src/Album/Model: 1 namespace AlbumModel; 2 3 4 5 6 7 class Album { public $id; public $artist; public $title; 8 public function exchangeArray($data) { $this->id = (!empty($data[’id’])) ? $data[’id’] : null; $this->artist = (!empty($data[’artist’])) ? $data[’artist’] : null; $this->title = (!empty($data[’title’])) ? $data[’title’] : null; } 9 10 11 12 13 14 15 } Our Album entity object is a simple PHP class. In order to work with ZendDbs TableGateway class, we need to implement the exchangeArray() method. This method simply copies the data from the passed in array to our entitys properties. We will add an input filter for use with our form later. Next, we create our AlbumTable.php file in module/Album/src/Album/Model directory like this: 1 namespace AlbumModel; 2 3 use ZendDbTableGatewayTableGateway; 4 5 6 7 class AlbumTable { protected $tableGateway; 8 public function __construct(TableGateway $tableGateway) { $this->tableGateway = $tableGateway; } 9 10 11 12 13 public function fetchAll() { $resultSet = $this->tableGateway->select(); return $resultSet; } 14 15 16 17 18 19 public function getAlbum($id) { $id = (int) $id; $rowset = $this->tableGateway->select(array(’id’ => $id)); $row = $rowset->current(); if (!$row) { throw new Exception("Could not find row $id"); 20 21 22 23 24 25 26 20 Chapter 8. Database and models
  • 59. Zend Framework 2 Documentation, Release 2.2.5 } return $row; 27 28 } 29 30 public function saveAlbum(Album $album) { $data = array( ’artist’ => $album->artist, ’title’ => $album->title, ); 31 32 33 34 35 36 37 $id = (int) $album->id; if ($id == 0) { $this->tableGateway->insert($data); } else { if ($this->getAlbum($id)) { $this->tableGateway->update($data, array(’id’ => $id)); } else { throw new Exception(’Album id does not exist’); } } 38 39 40 41 42 43 44 45 46 47 } 48 49 public function deleteAlbum($id) { $this->tableGateway->delete(array(’id’ => (int) $id)); } 50 51 52 53 54 } Theres a lot going on here. Firstly, we set the protected property $tableGateway to the TableGateway instance passed in the constructor. We will use this to perform operations on the database table for our albums. We then create some helper methods that our application will use to interface with the table gateway. fetchAll() retrieves all albums rows from the database as a ResultSet, getAlbum() retrieves a single row as an Album object, saveAlbum() either creates a new row in the database or updates a row that already exists and deleteAlbum() removes the row completely. The code for each of these methods is, hopefully, self-explanatory. 8.3 Using ServiceManager to configure the table gateway and inject into the AlbumTable In order to always use the same instance of our AlbumTable, we will use the ServiceManager to define how to create one. This is most easily done in the Module class where we create a method called getServiceConfig() which is automatically called by the ModuleManager and applied to the ServiceManager. Well then be able to retrieve it in our controller when we need it. To configure the ServiceManager, we can either supply the name of the class to be instantiated or a factory (closure or callback) that instantiates the object when the ServiceManager needs it. We start by implementing getServiceConfig() to provide a factory that creates an AlbumTable. Add this method to the bottom of the Module.php file in module/Album. 1 namespace Album; 2 3 4 5 6 // Add these import statements: use AlbumModelAlbum; use AlbumModelAlbumTable; use ZendDbResultSetResultSet; 8.3. Using ServiceManager to configure the table gateway and inject into the AlbumTable 21
  • 60. Zend Framework 2 Documentation, Release 2.2.5 7 use ZendDbTableGatewayTableGateway; 8 9 10 11 class Module { // getAutoloaderConfig() and getConfig() methods here 12 // Add this method: public function getServiceConfig() { return array( ’factories’ => array( ’AlbumModelAlbumTable’ => function($sm) { $tableGateway = $sm->get(’AlbumTableGateway’); $table = new AlbumTable($tableGateway); return $table; }, ’AlbumTableGateway’ => function ($sm) { $dbAdapter = $sm->get(’ZendDbAdapterAdapter’); $resultSetPrototype = new ResultSet(); $resultSetPrototype->setArrayObjectPrototype(new Album()); return new TableGateway(’album’, $dbAdapter, null, $resultSetPrototype); }, ), ); } 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 } This method returns an array of factories that are all merged together by the ModuleManager before passing to the ServiceManager. The factory for AlbumModelAlbumTable uses the ServiceManager to create an AlbumTableGateway to pass to the AlbumTable. We also tell the ServiceManager that an AlbumTableGateway is created by getting a ZendDbAdapterAdapter (also from the ServiceManager) and using it to create a TableGateway object. The TableGateway is told to use an Album object whenever it creates a new result row. The TableGateway classes use the prototype pattern for creation of result sets and entities. This means that instead of instantiating when required, the system clones a previously instantiated object. See PHP Constructor Best Practices and the Prototype Pattern for more details. Finally, we need to configure the ServiceManager so that it knows how to get a ZendDbAdapterAdapter. This is done using a factory called ZendDbAdapterAdapterServiceFactory which we can configure within the merged config system. Zend Framework 2s ModuleManager merges all the configuration from each modules module.config.php file and then merges in the files in config/autoload (*.global.php and then *.local.php files). Well add our database configuration information to global.php which you should commit to your version control system. You can use local.php (outside of the VCS) to store the credentials for your database if you want to. Modify config/autoload/global.php (in the Zend Skeleton root, not inside the Album module) with following code: 1 2 3 4 5 6 7 8 9 10 11 12 13 return array( ’db’ => array( ’driver’ => ’Pdo’, ’dsn’ => ’mysql:dbname=zf2tutorial;host=localhost’, ’driver_options’ => array( PDO::MYSQL_ATTR_INIT_COMMAND => ’SET NAMES ’UTF8’’ ), ), ’service_manager’ => array( ’factories’ => array( ’ZendDbAdapterAdapter’ => ’ZendDbAdapterAdapterServiceFactory’, ), 22 Chapter 8. Database and models
  • 61. Zend Framework 2 Documentation, Release 2.2.5 ), 14 15 ); You should put your database credentials in config/autoload/local.php so that they are not in the git repository (as local.php is ignored): 1 2 3 4 5 6 return array( ’db’ => array( ’username’ => ’YOUR USERNAME HERE’, ’password’ => ’YOUR PASSWORD HERE’, ), ); 8.4 Back to the controller Now that the ServiceManager can create an AlbumTable instance for us, we can add a method to the controller to retrieve it. Add getAlbumTable() to the AlbumController class: 1 2 3 4 5 6 7 8 9 // module/Album/src/Album/Controller/AlbumController.php: public function getAlbumTable() { if (!$this->albumTable) { $sm = $this->getServiceLocator(); $this->albumTable = $sm->get(’AlbumModelAlbumTable’); } return $this->albumTable; } You should also add: 1 protected $albumTable; to the top of the class. We can now call getAlbumTable() from within our controller whenever we need to interact with our model. If the service locator was configured correctly in Module.php, then we should get an instance of AlbumModelAlbumTable when calling getAlbumTable(). 8.5 Listing albums In order to list the albums, we need to retrieve them from the model and pass them to the view. To do this, we fill in indexAction() within AlbumController. Update the AlbumControllers indexAction() like this: 1 2 3 4 5 6 7 8 9 // module/Album/src/Album/Controller/AlbumController.php: // ... public function indexAction() { return new ViewModel(array( ’albums’ => $this->getAlbumTable()->fetchAll(), )); } // ... 8.4. Back to the controller 23
  • 62. Zend Framework 2 Documentation, Release 2.2.5 With Zend Framework 2, in order to set variables in the view, we return a ViewModel instance where the first parameter of the constructor is an array from the action containing data we need. These are then automatically passed to the view script. The ViewModel object also allows us to change the view script that is used, but the default is to use {controller name}/{action name}. We can now fill in the index.phtml view script: 1 2 <?php // module/Album/view/album/album/index.phtml: 3 4 5 6 7 8 9 10 $title = ’My albums’; $this->headTitle($title); ?> <h1><?php echo $this->escapeHtml($title); ?></h1> <p> <a href="<?php echo $this->url(’album’, array(’action’=>’add’));?>">Add new album</a> </p> 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <table class="table"> <tr> <th>Title</th> <th>Artist</th> <th>&nbsp;</th> </tr> <?php foreach ($albums as $album) : ?> <tr> <td><?php echo $this->escapeHtml($album->title);?></td> <td><?php echo $this->escapeHtml($album->artist);?></td> <td> <a href="<?php echo $this->url(’album’, array(’action’=>’edit’, ’id’ => $album->id));?>">Edit</a> <a href="<?php echo $this->url(’album’, array(’action’=>’delete’, ’id’ => $album->id));?>">Delete</a> </td> </tr> <?php endforeach; ?> </table> The first thing we do is to set the title for the page (used in the layout) and also set the title for the <head> section using the headTitle() view helper which will display in the browsers title bar. We then create a link to add a new album. The url() view helper is provided by Zend Framework 2 and is used to create the links we need. The first parameter to url() is the route name we wish to use for construction of the URL, and the second parameter is an array of all the variables to fit into the placeholders to use. In this case we use our album route which is set up to accept two placeholder variables: action and id. We iterate over the $albums that we assigned from the controller action. The Zend Framework 2 view system automatically ensures that these variables are extracted into the scope of the view script, so that we dont have to worry about prefixing them with $this-> as we used to have to do with Zend Framework 1; however you can do so if you wish. We then create a table to display each albums title and artist, and provide links to allow for editing and deleting the record. A standard foreach: loop is used to iterate over the list of albums, and we use the alternate form using a colon and endforeach; as it is easier to scan than to try and match up braces. Again, the url() view helper is used to create the edit and delete links. Note: We always use the escapeHtml() view helper to help protect ourselves from Cross Site Scripting (XSS) vulnerabilities (see http://en.wikipedia.org/wiki/Cross-site_scripting). 24 Chapter 8. Database and models
  • 63. Zend Framework 2 Documentation, Release 2.2.5 If you open http://zf2-tutorial.localhost/album you should see this: 8.5. Listing albums 25
  • 64. Zend Framework 2 Documentation, Release 2.2.5 26 Chapter 8. Database and models
  • 65. CHAPTER 9 Styling and Translations Weve picked up the SkeletonApplications styling, which is fine, but we need to change the title and remove the copyright message. The ZendSkeletonApplication is set up to use ZendI18ns translation functionality for all the text. It uses .po files that live in module/Application/language, and you need to use poedit to change the text. Start poedit and open module/Application/language/en_US.po. Click on Skeleton Application in the list of Original strings and then type in Tutorial as the translation. Press Save in the toolbar and poedit will create an en_US.mo file for us. If you find that no .mo file is generated, check Preferences -> Editor -> Behavior and see if the checkbox marked Automatically compile .mo file on save is checked. To remove the copyright message, we need to edit the Application modules layout.phtml view script: 1 2 3 4 // module/Application/view/layout/layout.phtml: // Remove this line: <p>&copy; 2005 - 2013 by Zend Technologies Ltd. <?php echo $this->translate(’All rights reserved.’) ?></p> 27
  • 66. Zend Framework 2 Documentation, Release 2.2.5 The page now looks ever so slightly better now! 28 Chapter 9. Styling and Translations
  • 67. CHAPTER 10 Forms and actions 10.1 Adding new albums We can now code up the functionality to add new albums. There are two bits to this part: • Display a form for user to provide details • Process the form submission and store to database We use ZendForm to do this. The ZendForm component manages the form and, form validation, we add a ZendInputFilter to our Album entity. We start by creating a new class AlbumFormAlbumForm that extends from ZendFormForm to define our form. Create a file called AlbumForm.php in module/Album/src/Album/Form: 1 namespace AlbumForm; 2 3 use ZendFormForm; 4 5 6 7 8 9 10 class AlbumForm extends Form { public function __construct($name = null) { // we want to ignore the name passed parent::__construct(’album’); 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 $this->add(array( ’name’ => ’id’, ’type’ => ’Hidden’, )); $this->add(array( ’name’ => ’title’, ’type’ => ’Text’, ’options’ => array( ’label’ => ’Title’, ), )); $this->add(array( ’name’ => ’artist’, ’type’ => ’Text’, ’options’ => array( 29
  • 68. Zend Framework 2 Documentation, Release 2.2.5 ’label’ => ’Artist’, 27 ), 28 )); $this->add(array( ’name’ => ’submit’, ’type’ => ’Submit’, ’attributes’ => array( ’value’ => ’Go’, ’id’ => ’submitbutton’, ), )); 29 30 31 32 33 34 35 36 37 } 38 39 } Within the constructor of AlbumForm we do several things. First, we set the name of the form as we call the parents constructor. We then set the form’s method, in this case, post. Finally, we create four form elements: the id, title, artist, and submit button. For each item we set various attributes and options, including the label to be displayed. We also need to set up validation for this form. In Zend Framework 2 this is done using an input filter, which can either be standalone or defined within any class that implements the InputFilterAwareInterface interface, such as a model entity. In our case, we are going to add the input filter to the Album class, which resides in the Album.php file in module/Album/src/Album/Model: 1 namespace AlbumModel; 2 3 4 5 6 // Add these import statements use ZendInputFilterInputFilter; use ZendInputFilterInputFilterAwareInterface; use ZendInputFilterInputFilterInterface; 7 8 9 10 11 12 13 class Album implements InputFilterAwareInterface { public $id; public $artist; public $title; protected $inputFilter; // <-- Add this variable 14 public function exchangeArray($data) { $this->id = (isset($data[’id’])) ? $data[’id’] : null; $this->artist = (isset($data[’artist’])) ? $data[’artist’] : null; $this->title = (isset($data[’title’])) ? $data[’title’] : null; } 15 16 17 18 19 20 21 // Add content to these methods: public function setInputFilter(InputFilterInterface $inputFilter) { throw new Exception("Not used"); } 22 23 24 25 26 27 public function getInputFilter() { if (!$this->inputFilter) { $inputFilter = new InputFilter(); 28 29 30 31 32 $inputFilter->add(array( ’name’ => ’id’, ’required’ => true, 33 34 35 30 Chapter 10. Forms and actions
  • 69. Zend Framework 2 Documentation, Release 2.2.5 ’filters’ => array( array(’name’ => ’Int’), ), 36 37 38 )); 39 40 $inputFilter->add(array( ’name’ => ’artist’, ’required’ => true, ’filters’ => array( array(’name’ => ’StripTags’), array(’name’ => ’StringTrim’), ), ’validators’ => array( array( ’name’ => ’StringLength’, ’options’ => array( ’encoding’ => ’UTF-8’, ’min’ => 1, ’max’ => 100, ), ), ), )); 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 $inputFilter->add(array( ’name’ => ’title’, ’required’ => true, ’filters’ => array( array(’name’ => ’StripTags’), array(’name’ => ’StringTrim’), ), ’validators’ => array( array( ’name’ => ’StringLength’, ’options’ => array( ’encoding’ => ’UTF-8’, ’min’ => 1, ’max’ => 100, ), ), ), )); 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 $this->inputFilter = $inputFilter; 79 } 80 81 return $this->inputFilter; 82 } 83 84 } The InputFilterAwareInterface defines two methods: setInputFilter() and getInputFilter(). We only need to implement getInputFilter() so we simply throw an exception in setInputFilter(). Within getInputFilter(), we instantiate an InputFilter and then add the inputs that we require. We add one input for each property that we wish to filter or validate. For the id field we add an Int filter as we only need integers. For the text elements, we add two filters, StripTags and StringTrim, to remove unwanted HTML and unnecessary white space. We also set them to be required and add a StringLength validator to ensure that the user doesnt enter more characters than we can store into the database. 10.1. Adding new albums 31
  • 70. Zend Framework 2 Documentation, Release 2.2.5 We now need to get the form to display and then process it on submission. AlbumControllers addAction(): 1 This is done within the // module/Album/src/Album/Controller/AlbumController.php: 2 3 4 5 6 7 8 //... use ZendMvcControllerAbstractActionController; use ZendViewModelViewModel; use AlbumModelAlbum; // <-- Add this import use AlbumFormAlbumForm; // <-- Add this import //... 9 // Add content to this method: public function addAction() { $form = new AlbumForm(); $form->get(’submit’)->setValue(’Add’); 10 11 12 13 14 15 $request = $this->getRequest(); if ($request->isPost()) { $album = new Album(); $form->setInputFilter($album->getInputFilter()); $form->setData($request->getPost()); 16 17 18 19 20 21 if ($form->isValid()) { $album->exchangeArray($form->getData()); $this->getAlbumTable()->saveAlbum($album); 22 23 24 25 // Redirect to list of albums return $this->redirect()->toRoute(’album’); 26 27 } } return array(’form’ => $form); 28 29 30 31 32 } //... After adding the AlbumForm to the use list, we implement addAction(). Lets look at the addAction() code in a little more detail: 1 2 $form = new AlbumForm(); $form->get(’submit’)->setValue(’Add’); We instantiate AlbumForm and set the label on the submit button to Add. We do this here as well want to re-use the form when editing an album and will use a different label. 1 2 3 4 5 6 $request = $this->getRequest(); if ($request->isPost()) { $album = new Album(); $form->setInputFilter($album->getInputFilter()); $form->setData($request->getPost()); if ($form->isValid()) { If the Request objects isPost() method is true, then the form has been submitted and so we set the forms input filter from an album instance. We then set the posted data to the form and check to see if it is valid using the isValid() member function of the form. 1 2 $album->exchangeArray($form->getData()); $this->getAlbumTable()->saveAlbum($album); If the form is valid, then we grab the data from the form and store to the model using saveAlbum(). 32 Chapter 10. Forms and actions
  • 71. Zend Framework 2 Documentation, Release 2.2.5 1 2 // Redirect to list of albums return $this->redirect()->toRoute(’album’); After we have saved the new album row, we redirect back to the list of albums using the Redirect controller plugin. 1 return array(’form’ => $form); Finally, we return the variables that we want assigned to the view. In this case, just the form object. Note that Zend Framework 2 also allows you to simply return an array containing the variables to be assigned to the view and it will create a ViewModel behind the scenes for you. This saves a little typing. We now need to render the form in the add.phtml view script: 1 2 <?php // module/Album/view/album/album/add.phtml: 3 4 5 6 7 8 9 10 $title = ’Add new album’; $this->headTitle($title); ?> <h1><?php echo $this->escapeHtml($title); ?></h1> <?php $form->setAttribute(’action’, $this->url(’album’, array(’action’ => ’add’))); $form->prepare(); 11 12 13 14 15 16 17 echo echo echo echo echo echo $this->form()->openTag($form); $this->formHidden($form->get(’id’)); $this->formRow($form->get(’title’)); $this->formRow($form->get(’artist’)); $this->formSubmit($form->get(’submit’)); $this->form()->closeTag(); Again, we display a title as before and then we render the form. Zend Framework provides some view helpers to make this a little easier. The form() view helper has an openTag() and closeTag() method which we use to open and close the form. Then for each element with a label, we can use formRow(), but for the two elements that are standalone, we use formHidden() and formSubmit(). 10.1. Adding new albums 33
  • 72. Zend Framework 2 Documentation, Release 2.2.5 Alternatively, the process of rendering the form can be simplified by using the bundled formCollection view helper. For example, in the view script above replace all the form-rendering echo statements with: 1 echo $this->formCollection($form); Note: You still need to call the openTag and closeTag methods of the form. You replace the other echo statements with the call to formCollection, above. This will iterate over the form structure, calling the appropriate label, element and error view helpers for each element, but you still have to wrap formCollection($form) with the open and close form tags. This helps reduce the complexity of your view script in situations where the default HTML rendering of the form is acceptable. You should now be able to use the Add new album link on the home page of the application to add a new album record. 10.2 Editing an album Editing an album is almost identical to adding one, so the code is very similar. This time we use editAction() in the AlbumController: 1 2 // module/Album/src/Album/Controller/AlbumController.php: //... 3 // Add content to this method: public function editAction() { $id = (int) $this->params()->fromRoute(’id’, 0); if (!$id) { return $this->redirect()->toRoute(’album’, array( ’action’ => ’add’ )); } 4 5 6 7 8 9 10 11 12 13 // Get the Album with the specified id. An exception is thrown // if it cannot be found, in which case go to the index page. try { $album = $this->getAlbumTable()->getAlbum($id); } catch (Exception $ex) { return $this->redirect()->toRoute(’album’, array( ’action’ => ’index’ )); } 14 15 16 17 18 19 20 21 22 23 24 $form = new AlbumForm(); $form->bind($album); $form->get(’submit’)->setAttribute(’value’, ’Edit’); 25 26 27 28 $request = $this->getRequest(); if ($request->isPost()) { $form->setInputFilter($album->getInputFilter()); $form->setData($request->getPost()); 29 30 31 32 33 if ($form->isValid()) { $this->getAlbumTable()->saveAlbum($album); 34 35 36 // Redirect to list of albums return $this->redirect()->toRoute(’album’); 37 38 34 Chapter 10. Forms and actions
  • 73. Zend Framework 2 Documentation, Release 2.2.5 } 39 } 40 41 return array( ’id’ => $id, ’form’ => $form, ); 42 43 44 45 46 47 } //... This code should look comfortably familiar. Lets look at the differences from adding an album. Firstly, we look for the id that is in the matched route and use it to load the album to be edited: 1 2 3 4 5 6 $id = (int) $this->params()->fromRoute(’id’, 0); if (!$id) { return $this->redirect()->toRoute(’album’, array( ’action’ => ’add’ )); } 7 8 9 10 11 12 13 14 15 16 17 // Get the album with the specified id. An exception is thrown // if it cannot be found, in which case go to the index page. try { $album = $this->getAlbumTable()->getAlbum($id); } catch (Exception $ex) { return $this->redirect()->toRoute(’album’, array( ’action’ => ’index’ )); } params is a controller plugin that provides a convenient way to retrieve parameters from the matched route. We use it to retrieve the id from the route we created in the modules module.config.php. If the id is zero, then we redirect to the add action, otherwise, we continue by getting the album entity from the database. We have to check to make sure that the Album with the specified id can actually be found. If it cannot, then the data access method throws an exception. We catch that exception and re-route the user to the index page. 1 2 3 $form = new AlbumForm(); $form->bind($album); $form->get(’submit’)->setAttribute(’value’, ’Edit’); The forms bind() method attaches the model to the form. This is used in two ways: • When displaying the form, the initial values for each element are extracted from the model. • After successful validation in isValid(), the data from the form is put back into the model. These operations are done using a hydrator object. There are a number of hydrators, but the default one is ZendStdlibHydratorArraySerializable which expects to find two methods in the model: getArrayCopy() and exchangeArray(). We have already written exchangeArray() in our Album entity, so just need to write getArrayCopy(): 1 2 3 4 5 6 7 // module/Album/src/Album/Model/Album.php: // ... public function exchangeArray($data) { $this->id = (isset($data[’id’])) ? $data[’id’] : null; $this->artist = (isset($data[’artist’])) ? $data[’artist’] : null; $this->title = (isset($data[’title’])) ? $data[’title’] : null; 10.2. Editing an album 35
  • 74. Zend Framework 2 Documentation, Release 2.2.5 } 8 9 10 11 12 13 14 15 // Add the following method: public function getArrayCopy() { return get_object_vars($this); } // ... As a result of using bind() with its hydrator, we do not need to populate the forms data back into the $album as thats already been done, so we can just call the mappers saveAlbum() to store the changes back to the database. The view template, edit.phtml, looks very similar to the one for adding an album: 1 2 <?php // module/Album/view/album/album/edit.phtml: 3 4 5 6 7 $title = ’Edit album’; $this->headTitle($title); ?> <h1><?php echo $this->escapeHtml($title); ?></h1> 8 9 10 11 12 13 14 15 16 17 18 <?php $form = $this->form; $form->setAttribute(’action’, $this->url( ’album’, array( ’action’ => ’edit’, ’id’ => $this->id, ) )); $form->prepare(); 19 20 21 22 23 24 25 echo echo echo echo echo echo $this->form()->openTag($form); $this->formHidden($form->get(’id’)); $this->formRow($form->get(’title’)); $this->formRow($form->get(’artist’)); $this->formSubmit($form->get(’submit’)); $this->form()->closeTag(); The only changes are to use the Edit Album title and set the forms action to the edit action too. You should now be able to edit albums. 10.3 Deleting an album To round out our application, we need to add deletion. We have a Delete link next to each album on our list page and the naive approach would be to do a delete when its clicked. This would be wrong. Remembering our HTTP spec, we recall that you shouldnt do an irreversible action using GET and should use POST instead. We shall show a confirmation form when the user clicks delete and if they then click yes, we will do the deletion. As the form is trivial, well code it directly into our view (ZendForm is, after all, optional!). Lets start with the action code in AlbumController::deleteAction(): 1 2 3 // module/Album/src/Album/Controller/AlbumController.php: //... // Add content to the following method: 36 Chapter 10. Forms and actions
  • 75. Zend Framework 2 Documentation, Release 2.2.5 4 5 6 7 8 9 public function deleteAction() { $id = (int) $this->params()->fromRoute(’id’, 0); if (!$id) { return $this->redirect()->toRoute(’album’); } 10 $request = $this->getRequest(); if ($request->isPost()) { $del = $request->getPost(’del’, ’No’); 11 12 13 14 if ($del == ’Yes’) { $id = (int) $request->getPost(’id’); $this->getAlbumTable()->deleteAlbum($id); } 15 16 17 18 19 // Redirect to list of albums return $this->redirect()->toRoute(’album’); 20 21 } 22 23 return array( ’id’ => $id, ’album’ => $this->getAlbumTable()->getAlbum($id) ); 24 25 26 27 28 29 } //... As before, we get the id from the matched route, and check the request objects isPost() to determine whether to show the confirmation page or to delete the album. We use the table object to delete the row using the deleteAlbum() method and then redirect back the list of albums. If the request is not a POST, then we retrieve the correct database record and assign to the view, along with the id. The view script is a simple form: 1 2 <?php // module/Album/view/album/album/delete.phtml: 3 4 5 6 7 $title = ’Delete album’; $this->headTitle($title); ?> <h1><?php echo $this->escapeHtml($title); ?></h1> 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <p>Are you sure that you want to delete ’<?php echo $this->escapeHtml($album->title); ?>’ by ’<?php echo $this->escapeHtml($album->artist); ?>’? </p> <?php $url = $this->url(’album’, array( ’action’ => ’delete’, ’id’ => $this->id, )); ?> <form action="<?php echo $url; ?>" method="post"> <div> <input type="hidden" name="id" value="<?php echo (int) $album->id; ?>" /> <input type="submit" name="del" value="Yes" /> <input type="submit" name="del" value="No" /> </div> </form> 10.3. Deleting an album 37
  • 76. Zend Framework 2 Documentation, Release 2.2.5 In this script, we display a confirmation message to the user and then a form with “Yes” and “No” buttons. In the action, we checked specifically for the Yes value when doing the deletion. 10.4 Ensuring that the home page displays the list of albums One final point. At the moment, the home page, http://zf2-tutorial.localhost/ doesnt display the list of albums. This is due to a route set up in the Application modules module.config.php. module/Application/config/module.config.php and find the home route: 1 2 3 4 5 6 7 8 9 10 To change it, open ’home’ => array( ’type’ => ’ZendMvcRouterHttpLiteral’, ’options’ => array( ’route’ => ’/’, ’defaults’ => array( ’controller’ => ’ApplicationControllerIndex’, ’action’ => ’index’, ), ), ), Change the controller from ApplicationControllerIndex to AlbumControllerAlbum: 1 2 3 4 5 6 7 8 9 10 ’home’ => array( ’type’ => ’ZendMvcRouterHttpLiteral’, ’options’ => array( ’route’ => ’/’, ’defaults’ => array( ’controller’ => ’AlbumControllerAlbum’, // <-- change here ’action’ => ’index’, ), ), ), Thats it - you now have a fully working application! 38 Chapter 10. Forms and actions
  • 77. CHAPTER 11 Conclusion This concludes our brief look at building a simple, but fully functional, MVC application using Zend Framework 2. In this tutorial we but briefly touched quite a number of different parts of the framework. The most important part of applications built with Zend Framework 2 are the modules, the building blocks of any MVC ZF2 application. To ease the work with dependencies inside our applications, we use the service manager. To be able to map a request to controllers and their actions, we use routes. Data persistence, in most cases, includes using ZendDb to communicate with one of the databases. Input data is filtered and validated with input filters and together with ZendForm they provide a strong bridge between the domain model and the view layer. ZendView is responsible for the View in the MVC stack, together with a vast amount of view helpers. 39
  • 78. Zend Framework 2 Documentation, Release 2.2.5 40 Chapter 11. Conclusion
  • 79. CHAPTER 12 Zend Framework Tool (ZFTool) ZFTool is a utility module for maintaining modular Zend Framework 2 applications. It runs from the command line and can be installed as ZF2 module or as PHAR (see below). This tool gives you the ability to: • create a ZF2 project, installing a skeleton application; • create a new module inside an existing ZF2 application; • get the list of all the modules installed inside an application; • get the configuration file of a ZF2 application; • install the ZF2 library choosing a specific version. To install the ZFTool you can use one of the following methods or you can just download the PHAR package and use it. 12.1 Installation using Composer 1. Open console (command prompt) 2. Go to your application’s directory 3. Run composer require zendframework/zftool:dev-master 12.2 Manual installation 1. Clone using git or download zipball 2. Extract to vendor/ZFTool in your ZF2 application 3. Enter the vendor/ZFTool folder and execute zf.php as reported below. 12.3 Without installation, using the PHAR file 1. You don’t need to install ZFTool if you want just use it as a shell command. You can download zftool.phar and use it. 41
  • 80. Zend Framework 2 Documentation, Release 2.2.5 12.4 Usage In the following usage examples, the zf.php command can be replaced with zftool.phar. 12.4.1 Basic information > zf.php modules [list] show loaded modules The modules option gives you the list of all the modules installed in a ZF2 application. > zf.php version | --version display current Zend Framework version The version option gives you the version number of ZFTool and, if executed from the root folder of a ZF2 application, the version number of the Zend Framework library used by the application. 12.4.2 Project creation > zf.php create project <path> <path> The path of the project to be created This command installs the ZendSkeletonApplication in the specified path. 12.4.3 Module creation > zf.php create module <name> [<path>] <name> <path> The name of the module to be created The path to the root folder of the ZF2 application (optional) This command can be used to create a new module inside an existing ZF2 application. If the path is not provided the ZFTool try to create a new module in the local directory (only if the local folder contains a ZF2 application). 12.4.4 Classmap generator > zf.php classmap generate <directory> <classmap file> [--append|-a] [--overwrite|-w] <directory> <classmap file> --append | -a --overwrite | -w The directory to scan for PHP classes (use "." to use current directory) File name for generated class map file or - for standard output. If not supplied autoload_classmap.php inside <directory>. Append to classmap file if it exists Whether or not to overwrite existing classmap file 12.4.5 ZF library installation > zf.php install zf <path> [<version>] <path> <version> 42 The directory where to install the ZF2 library The version to install, if not specified uses the last available Chapter 12. Zend Framework Tool (ZFTool)
  • 81. Zend Framework 2 Documentation, Release 2.2.5 This command install the specified version of the ZF2 library in a path. If the version is omitted it will be used the last stable available. Using this command you can install all the tag version specified in the ZF2 github repository (the name used for the version is obtained removing the ‘release-‘ string from the tag name; for instance, the tag ‘release-2.0.0’ is equivalent to the version number 2.0.0). 12.4.6 Compile the PHAR file You can create a .phar file containing the ZFTool project. In order to compile ZFTool in a .phar file you need to execute the following command: > bin/create-phar This command will create a zftool.phar file in the bin folder. You can use and ship only this file to execute all the ZFTool functionalities. After the zftool.phar creation, we suggest to add the folder bin of ZFTool in your PATH environment. In this way you can execute the zftool.phar script wherever you are. 12.4. Usage 43
  • 82. Zend Framework 2 Documentation, Release 2.2.5 44 Chapter 12. Zend Framework Tool (ZFTool)
  • 83. CHAPTER 13 Learning Dependency Injection 13.1 Very brief introduction to Di. Dependency Injection is a concept that has been talked about in numerous places over the web. For the purposes of this quickstart, we’ll explain the act of injecting dependencies simply with this below code: 1 $b = new B(new A()); Above, A is a dependency of B, and A was injected into B. If you are not familiar with the concept of dependency injection, here are a couple of great reads: Matthew Weier O’Phinney’s Analogy, Ralph Schindler’s Learning DI, or Fabien Potencier’s Series on DI. 13.2 Simplest usage case (2 classes, one consumes the other) In the simplest use case, a developer might have one class (A) that is consumed by another class (B) through the constructor. By having the dependency injected through the constructor, this requires an object of type A be instantiated before an object of type B so that A can be injected into B. 1 namespace My { 2 class A { /* Some useful functionality */ } 3 4 5 6 7 class B { protected $a = null; public function __construct(A $a) { $this->a = $a; } } 8 9 10 11 12 13 14 15 16 } To create B by hand, a developer would follow this work flow, or a similar workflow to this: 45
  • 84. Zend Framework 2 Documentation, Release 2.2.5 1 $b = new B(new A()); If this workflow becomes repeated throughout your application multiple times, this creates an opportunity where one might want to DRY up the code. While there are several ways to do this, using a dependency injection container is one of these solutions. With Zend’s dependency injection container ZendDiDi, the above use case can be taken care of with no configuration (provided all of your autoloading is already configured properly) with the following usage: 1 2 $di = new ZendDiDi; $b = $di->get(’MyB’); // will produce a B object that is consuming an A object Moreover, by using the Di::get() method, you are ensuring that the same exact object is returned on subsequent calls. To force new objects to be created on each and every request, one would use the Di::newInstance() method: 1 $b = $di->newInstance(’MyB’); Let’s assume for a moment that A requires some configuration before it can be created. Our previous use case is expanded to this (we’ll throw a 3rd class in for good measure): 1 namespace My { 2 class A { protected $username = null; protected $password = null; public function __construct($username, $password) { $this->username = $username; $this->password = $password; } } 3 4 5 6 7 8 9 10 11 12 13 class B { protected $a = null; public function __construct(A $a) { $this->a = $a; } } 14 15 16 17 18 19 20 21 22 class C { protected $b = null; public function __construct(B $b) { $this->b = $b; } } 23 24 25 26 27 28 29 30 31 32 } With the above, we need to ensure that our Di is capable of setting the A class with a few configuration values (which are generally scalar in nature). To do this, we need to interact with the InstanceManager: 1 2 3 $di = new ZendDiDi; $di->getInstanceManager()->setProperty(’A’, ’username’, ’MyUsernameValue’); $di->getInstanceManager()->setProperty(’A’, ’password’, ’MyHardToGuessPassword%$#’); 46 Chapter 13. Learning Dependency Injection
  • 85. Zend Framework 2 Documentation, Release 2.2.5 Now that our container has values it can use when creating A, and our new goal is to have a C object that consumes B and in turn consumes A, the usage scenario is still the same: 1 2 3 $c = $di->get(’MyC’); // or $c = $di->newInstance(’MyC’); Simple enough, but what if we wanted to pass in these parameters at call time? Assuming a default Di object ($di = new ZendDiDi() without any configuration to the InstanceManager), we could do the following: 1 2 3 4 $parameters = array( ’username’ => ’MyUsernameValue’, ’password’ => ’MyHardToGuessPassword%$#’, ); 5 6 7 8 $c = $di->get(’MyC’, $parameters); // or $c = $di->newInstance(’MyC’, $parameters); Constructor injection is not the only supported type of injection. The other most popular method of injection is also supported: setter injection. Setter injection allows one to have a usage scenario that is the same as our previous example with the exception, for example, of our B class now looking like this: 1 2 3 4 5 6 7 8 9 10 namespace My { class B { protected $a; public function setA(A $a) { $this->a = $a; } } } Since the method is prefixed with set, and is followed by a capital letter, the Di knows that this method is used for setter injection, and again, the use case $c = $di->get(’C’), will once again know how to fill the dependencies when needed to create an object of type C. Other methods are being created to determine what the wirings between classes are, such as interface injection and annotation based injection. 13.3 Simplest Usage Case Without Type-hints If your code does not have type-hints or you are using 3rd party code that does not have type-hints but does practice dependency injection, you can still use the Di, but you might find you need to describe your dependencies explicitly. To do this, you will need to interact with one of the definitions that is capable of letting a developer describe, with objects, the map between classes. This particular definition is called the BuilderDefinition and can work with, or in place of, the default RuntimeDefinition. Definitions are a part of the Di that attempt to describe the relationship between classes so that Di::newInstance() and Di::get() can know what the dependencies are that need to be filled for a particular class/object. With no configuration, Di will use the RuntimeDefinition which uses reflection and the type-hints in your code to determine the dependency map. Without type-hints, it will assume that all dependencies are scalar or required configuration parameters. The BuilderDefinition, which can be used in tandem with the RuntimeDefinition (technically, it can be used in tandem with any definition by way of the AggregateDefinition), allows you to programmatically 13.3. Simplest Usage Case Without Type-hints 47
  • 86. Zend Framework 2 Documentation, Release 2.2.5 describe the mappings with objects. Let’s say for example, our above A/B/C usage scenario, were altered such that class B now looks like this: 1 2 3 4 5 6 7 8 9 10 namespace My { class B { protected $a; public function setA($a) { $this->a = $a; } } } You’ll notice the only change is that setA now does not include any type-hinting information. 1 2 3 use ZendDiDi; use ZendDiDefinition; use ZendDiDefinitionBuilder; 4 5 6 7 // Describe this class: $builder = new DefinitionBuilderDefinition; $builder->addClass(($class = new BuilderPhpClass)); 8 9 10 $class->setName(’MyB’); $class->addInjectableMethod(($im = new BuilderInjectableMethod)); 11 12 13 $im->setName(’setA’); $im->addParameter(’a’, ’MyA’); 14 15 16 17 18 19 // Use both our Builder Definition as well as the default // RuntimeDefinition, builder first $aDef = new DefinitionAggregateDefinition; $aDef->addDefinition($builder); $aDef->addDefinition(new DefinitionRuntimeDefinition); 20 21 22 23 // Now make sure the Di understands it $di = new Di; $di->setDefinition($aDef); 24 25 26 27 28 29 // and finally, create C $parameters = array( ’username’ => ’MyUsernameValue’, ’password’ => ’MyHardToGuessPassword%$#’, ); 30 31 $c = $di->get(’MyC’, $parameters); This above usage scenario provides that whatever the code looks like, you can ensure that it works with the dependency injection container. In an ideal world, all of your code would have the proper type hinting and/or would be using a mapping strategy that reduces the amount of bootstrapping work that needs to be done in order to have a full definition that is capable of instantiating all of the objects you might require. 13.4 Simplest usage case with Compiled Definition Without going into the gritty details, as you might expect, PHP at its core is not DI friendly. Out-of-the-box, the Di uses a RuntimeDefinition which does all class map resolution via PHP’s Reflection extension. Couple that 48 Chapter 13. Learning Dependency Injection
  • 87. Zend Framework 2 Documentation, Release 2.2.5 with the fact that PHP does not have a true application layer capable of storing objects in-memory between requests, and you get a recipe that is less performant than similar solutions you’ll find in Java and .Net (where there is an application layer with in-memory object storage.) To mitigate this shortcoming, ZendDi has several features built in capable of pre-compiling the most expensive tasks that surround dependency injection. It is worth noting that the RuntimeDefinition, which is used by default, is the only definition that does lookups on-demand. The rest of the Definition objects are capable of being aggregated and stored to disk in a very performant way. Ideally, 3rd party code will ship with a pre-compiled Definition that will describe the various relationships and parameter/property needs of each class that is to be instantiated. This Definition would have been built as part of some deployment or packaging task by this 3rd party. When this is not the case, you can create these Definitions via any of the Definition types provided with the exception of the RuntimeDefinition. Here is a breakdown of the job of each definition type: • AggregateDefinition- Aggregates multiple definitions of various types. When looking for a class, it looks it up in the order the definitions were provided to this aggregate. • ArrayDefinition- This definition takes an array of information and exposes it via the interface provided by ZendDiDefinition suitable for usage by Di or an AggregateDefinition • BuilderDefinition- Creates a definition based on an object graph consisting of various BuilderPhpClass objects and BuilderInjectionMethod objects that describe the mapping needs of the target codebase and • Compiler- This is not actually a definition, but produces an ArrayDefinition based off of a code scanner (ZendCodeScannerDirectoryScanner or ZendCodeScannerFileScanner). The following is an example of producing a definition via a DirectoryScanner: 1 2 3 4 5 $compiler = new ZendDiDefinitionCompiler(); $compiler->addCodeScannerDirectory( new ZendCodeScannerScannerDirectory(’path/to/library/My/’) ); $definition = $compiler->compile(); This definition can then be directly used by the Di (assuming the above A, B, C scenario was actually a file per class on disk): 1 2 3 4 5 $di = new ZendDiDi; $di->setDefinition($definition); $di->getInstanceManager()->setProperty(’MyA’, ’username’, ’foo’); $di->getInstanceManager()->setProperty(’MyA’, ’password’, ’bar’); $c = $di->get(’MyC’); One strategy for persisting these compiled definitions would be the following: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 if (!file_exists(__DIR__ . ’/di-definition.php’) && $isProduction) { $compiler = new ZendDiDefinitionCompiler(); $compiler->addCodeScannerDirectory( new ZendCodeScannerScannerDirectory(’path/to/library/My/’) ); $definition = $compiler->compile(); file_put_contents( __DIR__ . ’/di-definition.php’, ’<?php return ’ . var_export($definition->toArray(), true) . ’;’ ); } else { $definition = new ZendDiDefinitionArrayDefinition( include __DIR__ . ’/di-definition.php’ ); 13.4. Simplest usage case with Compiled Definition 49
  • 88. Zend Framework 2 Documentation, Release 2.2.5 15 } 16 17 18 // $definition can now be used; in a production system it will be written // to disk. Since ZendCodeScanner does not include files, the classes contained within are not loaded into memory. Instead, ZendCodeScanner uses tokenization to determine the structure of your files. This makes this suitable to use this solution during development and within the same request as any one of your application’s dispatched actions. 13.5 Creating a precompiled definition for others to use If you are a 3rd party code developer, it makes sense to produce a Definition file that describes your code so that others can utilize this Definition without having to Reflect it via the RuntimeDefinition, or create it via the Compiler. To do this, use the same technique as above. Instead of writing the resulting array to disk, you would write the information into a definition directly, by way of ZendCodeGenerator: 1 2 3 4 5 6 7 // First, compile the information $compiler = new ZendDiDefinitionCompilerDefinition(); $compiler->addDirectoryScanner( new ZendCodeScannerDirectoryScanner(__DIR__ . ’/My/’) ); $compiler->compile(); $definition = $compiler->toArrayDefinition(); 8 9 10 11 12 13 14 15 16 17 18 19 20 21 // Now, create a Definition class for this information $codeGenerator = new ZendCodeGeneratorFileGenerator(); $codeGenerator->setClass(($class = new ZendCodeGeneratorClassGenerator())); $class->setNamespaceName(’My’); $class->setName(’DiDefinition’); $class->setExtendedClass(’ZendDiDefinitionArrayDefinition’); $class->addMethod( ’__construct’, array(), ZendCodeGeneratorMethodGenerator::FLAG_PUBLIC, ’parent::__construct(’ . var_export($definition->toArray(), true) . ’);’ ); file_put_contents(__DIR__ . ’/My/DiDefinition.php’, $codeGenerator->generate()); 13.6 Using Multiple Definitions From Multiple Sources In all actuality, you will be using code from multiple places, some Zend Framework code, some other 3rd party code, and of course, your own code that makes up your application. Here is a method for consuming definitions from multiple places: 1 2 3 use ZendDiDi; use ZendDiDefinition; use ZendDiDefinitionBuilder; 4 5 6 $di = new Di; $diDefAggregate = new DefinitionAggregate(); 7 8 9 // first add in provided Definitions, for example $diDefAggregate->addDefinition(new ThirdPartyDbalDiDefinition()); 50 Chapter 13. Learning Dependency Injection
  • 89. Zend Framework 2 Documentation, Release 2.2.5 10 $diDefAggregate->addDefinition(new ZendControllerDiDefinition()); 11 12 13 14 15 16 17 18 19 20 21 // for code that does not have TypeHints $builder = new DefinitionBuilderDefinition(); $builder->addClass(($class = BuilderPhpClass)); $class->addInjectionMethod( ($injectMethod = new BuilderInjectionMethod()) ); $injectMethod->setName(’injectImplementation’); $injectMethod->addParameter( ’implementation’, ’ClassForSpecificImplementation’ ); 22 23 24 25 26 27 28 29 // now, your application code $compiler = new DefinitionCompiler() $compiler->addCodeScannerDirectory( new ZendCodeScannerDirectoryScanner(__DIR__ . ’/App/’) ); $appDefinition = $compiler->compile(); $diDefAggregate->addDefinition($appDefinition); 30 31 32 // now, pass in properties $im = $di->getInstanceManager(); 33 34 35 36 37 38 39 40 41 42 43 44 // this could come from ZendConfigConfig::toArray $propertiesFromConfig = array( ’ThirdPartyDbalDbAdapter’ => array( ’username’ => ’someUsername’, ’password’ => ’somePassword’ ), ’ZendControllerHelperContentType’ => array( ’default’ => ’xhtml5’ ), ); $im->setProperties($propertiesFromConfig); 13.7 Generating Service Locators In production, you want things to be as fast as possible. The Dependency Injection Container, while engineered for speed, still must do a fair bit of work resolving parameters and dependencies at runtime. What if you could speed things up and remove those lookups? The ZendDiServiceLocatorGenerator component can do just that. It takes a configured DI instance, and generates a service locator class for you from it. That class will manage instances for you, as well as provide hard-coded, lazy-loading instantiation of instances. The method getCodeGenerator() returns an instance of ZendCodeGeneratorPhpPhpFile, from which you can then write a class file with the new Service Locator. Methods on the Generator class allow you to specify the namespace and class for the generated Service Locator. As an example, consider the following: 1 use ZendDiServiceLocatorGenerator; 2 3 4 // $di is a fully configured DI instance $generator = new Generator($di); 13.7. Generating Service Locators 51
  • 90. Zend Framework 2 Documentation, Release 2.2.5 5 6 7 8 9 10 $generator->setNamespace(’Application’) ->setContainerClass(’Context’); $file = $generator->getCodeGenerator(); $file->setFilename(__DIR__ . ’/../Application/Context.php’); $file->write(); The above code will write to ../Application/Context.php, and that file will contain the class ApplicationContext. That file might look like the following: 1 <?php 2 3 namespace Application; 4 5 use ZendDiServiceLocator; 6 7 8 class Context extends ServiceLocator { 9 public function get($name, array $params = array()) { switch ($name) { case ’composed’: case ’MyComposedClass’: return $this->getMyComposedClass(); 10 11 12 13 14 15 16 case ’struct’: case ’MyStruct’: return $this->getMyStruct(); 17 18 19 20 default: return parent::get($name, $params); 21 22 } 23 } 24 25 public function getComposedClass() { if (isset($this->services[’MyComposedClass’])) { return $this->services[’MyComposedClass’]; } 26 27 28 29 30 31 $object = new MyComposedClass(); $this->services[’MyComposedClass’] = $object; return $object; 32 33 34 } public function getMyStruct() { if (isset($this->services[’MyStruct’])) { return $this->services[’MyStruct’]; } 35 36 37 38 39 40 41 $object = new MyStruct(); $this->services[’MyStruct’] = $object; return $object; 42 43 44 } 45 46 public function getComposed() { return $this->get(’MyComposedClass’); 47 48 49 52 Chapter 13. Learning Dependency Injection
  • 91. Zend Framework 2 Documentation, Release 2.2.5 } 50 51 public function getStruct() { return $this->get(’MyStruct’); } 52 53 54 55 56 } To use this class, you simply consume it as you would a DI container: 1 $container = new ApplicationContext; 2 3 $struct = $container->get(’struct’); // MyStruct instance One note about this functionality in its current incarnation. Configuration is per-environment only at this time. This means that you will need to generate a container per execution environment. Our recommendation is that you do so, and then in your environment, specify the container class to use. 13.7. Generating Service Locators 53
  • 92. Zend Framework 2 Documentation, Release 2.2.5 54 Chapter 13. Learning Dependency Injection
  • 93. CHAPTER 14 Unit Testing a Zend Framework 2 application A solid unit test suite is essential for ongoing development in large projects, especially those with many people involved. Going back and manually testing every individual component of an application after every change is impractical. Your unit tests will help alleviate that by automatically testing your application’s components and alerting you when something is not working the same way it was when you wrote your tests. This tutorial is written in the hopes of showing how to test different parts of a Zend Framework 2 MVC application. As such, this tutorial will use the application written in the getting started user guide. It is in no way a guide to unit testing in general, but is here only to help overcome the initial hurdles in writing unit tests for ZF2 applications. It is recommended to have at least a basic understanding of unit tests, assertions and mocks. As the Zend Framework 2 API uses PHPUnit, so will this tutorial. This tutorial assumes that you already have PHPUnit installed. The version of PHPUnit used should be 3.7.* 14.1 Setting up the tests directory As Zend Framework 2 applications are built from modules that should be standalone blocks of an application, we don’t test the application in it’s entirety, but module by module. We will show how to set up the minimum requirements to test a module, the Album module we wrote in the user guide, and which then can be used as a base for testing any other module. Start by creating a directory called test in zf2-tutorialmoduleAlbum with the following subdirectories: zf2-tutorial/ /module /Album /test /AlbumTest /Controller The structure of the test directory matches exactly with that of the module’s source files, and it will allow you to keep your tests well-organized and easy to find. 55
  • 94. Zend Framework 2 Documentation, Release 2.2.5 14.2 Bootstrapping your tests Next, create a file called phpunit.xml under zf2-tutorial/module/Album/test: <?xml version="1.0" encoding="UTF-8"?> <phpunit bootstrap="Bootstrap.php" colors="true"> <testsuites> <testsuite name="zf2tutorial"> <directory>./AlbumTest</directory> </testsuite> </testsuites> </phpunit> And a file called Bootstrap.php, also under zf2-tutorial/module/Album/test: 1 <?php 2 3 namespace AlbumTest; 4 5 6 7 8 use use use use ZendLoaderAutoloaderFactory; ZendMvcServiceServiceManagerConfig; ZendServiceManagerServiceManager; RuntimeException; 9 10 11 error_reporting(E_ALL | E_STRICT); chdir(__DIR__); 12 13 14 15 16 17 18 /** * Test bootstrap, for setting up autoloading */ class Bootstrap { protected static $serviceManager; 19 public static function init() { $zf2ModulePaths = array(dirname(dirname(__DIR__))); if (($path = static::findParentPath(’vendor’))) { $zf2ModulePaths[] = $path; } if (($path = static::findParentPath(’module’)) !== $zf2ModulePaths[0]) { $zf2ModulePaths[] = $path; } 20 21 22 23 24 25 26 27 28 29 static::initAutoloader(); 30 31 // use ModuleManager to load this module and it’s dependencies $config = array( ’module_listener_options’ => array( ’module_paths’ => $zf2ModulePaths, ), ’modules’ => array( ’Album’ ) ); 32 33 34 35 36 37 38 39 40 41 $serviceManager = new ServiceManager(new ServiceManagerConfig()); 42 56 Chapter 14. Unit Testing a Zend Framework 2 application
  • 95. Zend Framework 2 Documentation, Release 2.2.5 $serviceManager->setService(’ApplicationConfig’, $config); $serviceManager->get(’ModuleManager’)->loadModules(); static::$serviceManager = $serviceManager; 43 44 45 46 } 47 48 49 50 51 52 public static function chroot() { $rootPath = dirname(static::findParentPath(’module’)); chdir($rootPath); } 53 54 55 56 57 public static function getServiceManager() { return static::$serviceManager; } 58 59 60 61 protected static function initAutoloader() { $vendorPath = static::findParentPath(’vendor’); 62 $zf2Path = getenv(’ZF2_PATH’); if (!$zf2Path) { if (defined(’ZF2_PATH’)) { $zf2Path = ZF2_PATH; } elseif (is_dir($vendorPath $zf2Path = $vendorPath . } elseif (is_dir($vendorPath $zf2Path = $vendorPath . } } 63 64 65 66 67 68 69 70 71 72 . ’/ZF2/library’)) { ’/ZF2/library’; . ’/zendframework/zendframework/library’)) { ’/zendframework/zendframework/library’; 73 if (!$zf2Path) { throw new RuntimeException( ’Unable to load ZF2. Run ‘php composer.phar install‘ or’ . ’ define a ZF2_PATH environment variable.’ ); } 74 75 76 77 78 79 80 if (file_exists($vendorPath . ’/autoload.php’)) { include $vendorPath . ’/autoload.php’; } 81 82 83 84 include $zf2Path . ’/Zend/Loader/AutoloaderFactory.php’; AutoloaderFactory::factory(array( ’ZendLoaderStandardAutoloader’ => array( ’autoregister_zf’ => true, ’namespaces’ => array( __NAMESPACE__ => __DIR__ . ’/’ . __NAMESPACE__, ), ), )); 85 86 87 88 89 90 91 92 93 94 } 95 96 97 98 99 100 protected static function findParentPath($path) { $dir = __DIR__; $previousDir = ’.’; while (!is_dir($dir . ’/’ . $path)) { 14.2. Bootstrapping your tests 57
  • 96. Zend Framework 2 Documentation, Release 2.2.5 $dir = dirname($dir); if ($previousDir === $dir) { return false; } $previousDir = $dir; 101 102 103 104 105 } return $dir . ’/’ . $path; 106 107 } 108 109 } 110 111 112 Bootstrap::init(); Bootstrap::chroot(); The contents of this bootstrap file can be daunting at first sight, but all it really does is ensuring that all the necessary files are autoloadable for our tests. The most important lines is line 38 on which we say what modules we want to load for our test. In this case we are only loading the Album module as it has no dependencies against other modules. Now, if you navigate to the zf2-tutorial/module/Album/test/ directory, and run phpunit, you should get a similar output to this: PHPUnit 3.7.13 by Sebastian Bergmann. Configuration read from /var/www/zf2-tutorial/module/Album/test/phpunit.xml Time: 0 seconds, Memory: 1.75Mb No tests executed! Even though no tests were executed, we at least know that the autoloader found the ZF2 files, otherwise it would throw a RuntimeException, defined on line 69 of our bootstrap file. 14.3 Your first controller test Testing controllers is never an easy task, but Zend Framework 2 comes with the ZendTest component which should make testing much less cumbersome. First, create IndexControllerTest.php under zf2-tutorial/module/Album/test/AlbumTest/Controller with the following contents: <?php namespace AlbumTestController; use ZendTestPHPUnitControllerAbstractHttpControllerTestCase; class AlbumControllerTest extends AbstractHttpControllerTestCase { public function setUp() { $this->setApplicationConfig( include ’/var/www/zf2-tutorial/config/application.config.php’ ); parent::setUp(); } } 58 Chapter 14. Unit Testing a Zend Framework 2 application
  • 97. Zend Framework 2 Documentation, Release 2.2.5 The AbstractHttpControllerTestCase class we extend here helps us setting up the application itself, helps with dispatching and other tasks that happen during a request, as well offers methods for asserting request params, response headers, redirects and more. See ZendTest documentation for more. One thing that is needed is to set the application config with the setApplicationConfig method. Now, add the following function to the AlbumControllerTest class: public function testIndexActionCanBeAccessed() { $this->dispatch(’/album’); $this->assertResponseStatusCode(200); $this->assertModuleName(’Album’); $this->assertControllerName(’AlbumControllerAlbum’); $this->assertControllerClass(’AlbumController’); $this->assertMatchedRouteName(’album’); } This test case dispatches the /album URL, asserts that the response code is 200, and that we ended up in the desired module and controller. Note: For asserting the controller name we are using the controller name we defined in our routing configuration for the Album module. In our example this should be defined on line 19 of the module.config.php file in the Album module. 14.4 A failing test case Finally, cd to zf2-tutorial/module/Album/test/ and run phpunit. Uh-oh! The test failed! PHPUnit 3.7.13 by Sebastian Bergmann. Configuration read from /var/www/zf2-tutorial/module/Album/test/phpunit.xml F Time: 0 seconds, Memory: 8.50Mb There was 1 failure: 1) AlbumTestControllerAlbumControllerTest::testIndexActionCanBeAccessed Failed asserting response code "200", actual status code is "500" /var/www/zf2-tutorial/vendor/ZF2/library/Zend/Test/PHPUnit/Controller/AbstractControllerTestCase.php: /var/www/zf2-tutorial/module/Album/test/AlbumTest/Controller/AlbumControllerTest.php:22 FAILURES! Tests: 1, Assertions: 0, Failures: 1. The failure message doesn’t tell us much, apart from that the expected status code is not 200, but 500. To get a bit more information when something goes wrong in a test case, we set the protected $traceError member to true. Add the following just above the setUp method in our AlbumControllerTest class: protected $traceError = true; 14.4. A failing test case 59
  • 98. Zend Framework 2 Documentation, Release 2.2.5 Running the phpunit command again and we should see some more information about what went wrong in our test. The main error message we are interested in should read something like: ZendServiceManagerExceptionServiceNotFoundException: ZendServiceManagerServiceManager::get was unable to fetch or create an instance for ZendDbAdapterAdapter From this error message it is clear that not all our dependencies are available in the service manager. Let us take a look how can we fix this. 14.5 Configuring the service manager for the tests The error says that the service manager can not create an instance of a database adapter for us. The database adapter is indirectly used by our AlbumModelAlbumTable to fetch the list of albums from the database. The first thought would be to create an instance of an adapter, pass it to the service manager and let the code run from there as is. The problem with this approach is that we would end up with our test cases actually doing queries against the database. To keep our tests fast, and to reduce the number of possible failure points in our tests, this should be avoided. The second thought would be then to create a mock of the database adapter, and prevent the actual database calls by mocking them out. This is a much better approach, but creating the adapter mock is tedious (but no doubt we will have to create it at one point). The best thing to do would be to mock out our AlbumModelAlbumTable class which retrieves the list of albums from the database. Remember, we are now testing our controller, so we can mock out the actual call to fetchAll and replace the return values with dummy values. At this point, we are not interested in how fetchAll retrieves the albums, but only that it gets called and that it returns an array of albums, so that is why we can get away with this mocking. When we will test AlbumTable itself, then we will write the actual tests for the fetchAll method. Here is how we can accomplish this, by modifying the testIndexActionCanBeAccessed test method as follows: 1 2 3 4 5 public function testIndexActionCanBeAccessed() { $albumTableMock = $this->getMockBuilder(’AlbumModelAlbumTable’) ->disableOriginalConstructor() ->getMock(); 6 $albumTableMock->expects($this->once()) ->method(’fetchAll’) ->will($this->returnValue(array())); 7 8 9 10 $serviceManager = $this->getApplicationServiceLocator(); $serviceManager->setAllowOverride(true); $serviceManager->setService(’AlbumModelAlbumTable’, $albumTableMock); 11 12 13 14 $this->dispatch(’/album’); $this->assertResponseStatusCode(200); 15 16 17 $this->assertModuleName(’Album’); $this->assertControllerName(’AlbumControllerAlbum’); $this->assertControllerClass(’AlbumController’); $this->assertMatchedRouteName(’album’); 18 19 20 21 22 } By default, the Service Manager does not allow us to replace existing services. As the AlbumModelAlbumTable was already set, we are allowing for overrides (line 12), and then replacing the real instance of the AlbumTable with 60 Chapter 14. Unit Testing a Zend Framework 2 application
  • 99. Zend Framework 2 Documentation, Release 2.2.5 a mock. The mock is created so that it will return just an empty array when the fetchAll method is called. This allows us to test for what we care about in this test, and that is that by dispatching to the /album URL we get to the Album module’s AlbumController. Running the phpunit command at this point, we will get the following output as the tests now pass: PHPUnit 3.7.13 by Sebastian Bergmann. Configuration read from /var/www/zf2-tutorial/module/Album/test/phpunit.xml . Time: 0 seconds, Memory: 9.00Mb OK (1 test, 6 assertions) 14.6 Testing actions with POST One of the most common actions happening in controllers is submitting a form with some POST data. Testing this is surprisingly easy: public function testAddActionRedirectsAfterValidPost() { $albumTableMock = $this->getMockBuilder(’AlbumModelAlbumTable’) ->disableOriginalConstructor() ->getMock(); $albumTableMock->expects($this->once()) ->method(’saveAlbum’) ->will($this->returnValue(null)); $serviceManager = $this->getApplicationServiceLocator(); $serviceManager->setAllowOverride(true); $serviceManager->setService(’AlbumModelAlbumTable’, $albumTableMock); $postData = array( ’title’ => ’Led Zeppelin III’, ’artist’ => ’Led Zeppelin’, ); $this->dispatch(’/album/add’, ’POST’, $postData); $this->assertResponseStatusCode(302); $this->assertRedirectTo(’/album’); } Here we test that when we make a POST request against the /album/add URL, the AlbumModelAlbumTable‘s saveAlbum will be called and after that we will be redirected back to the /album URL. Running phpunit gives us the following output: PHPUnit 3.7.13 by Sebastian Bergmann. Configuration read from /home/robert/www/zf2-tutorial/module/Album/test/phpunit.xml .. 14.6. Testing actions with POST 61
  • 100. Zend Framework 2 Documentation, Release 2.2.5 Time: 0 seconds, Memory: 10.75Mb OK (2 tests, 9 assertions) Testing the editAction and deleteAction methods can be easily done in a manner similar as shown for the addAction. 14.7 Testing model entities Now that we know how to test our controllers, let us move to an other important part of our application - the model entity. Here we want to test that the initial state of the entity is what we expect it to be, that we can convert the model’s parameters to and from an array, and that it has all the input filters we need. Create the file AlbumTest.php in module/Album/test/AlbumTest/Model directory with the following contents: 1 2 <?php namespace AlbumTestModel; 3 4 5 use AlbumModelAlbum; use PHPUnit_Framework_TestCase; 6 7 8 9 10 11 class AlbumTest extends PHPUnit_Framework_TestCase { public function testAlbumInitialState() { $album = new Album(); 12 $this->assertNull( $album->artist, ’"artist" should initially be null’ ); $this->assertNull( $album->id, ’"id" should initially be null’ ); $this->assertNull( $album->title, ’"title" should initially be null’ ); 13 14 15 16 17 18 19 20 21 22 23 24 } 25 26 public function testExchangeArraySetsPropertiesCorrectly() { $album = new Album(); $data = array(’artist’ => ’some artist’, ’id’ => 123, ’title’ => ’some title’); 27 28 29 30 31 32 33 $album->exchangeArray($data); 34 35 $this->assertSame( $data[’artist’], $album->artist, ’"artist" was not set correctly’ 36 37 38 39 62 Chapter 14. Unit Testing a Zend Framework 2 application
  • 101. Zend Framework 2 Documentation, Release 2.2.5 ); $this->assertSame( $data[’id’], $album->id, ’"id" was not set correctly’ ); $this->assertSame( $data[’title’], $album->title, ’"title" was not set correctly’ ); 40 41 42 43 44 45 46 47 48 49 50 51 } 52 53 54 55 public function testExchangeArraySetsPropertiesToNullIfKeysAreNotPresent() { $album = new Album(); 56 $album->exchangeArray(array(’artist’ => ’some artist’, ’id’ => 123, ’title’ => ’some title’)); $album->exchangeArray(array()); 57 58 59 60 61 $this->assertNull( $album->artist, ’"artist" should have defaulted to null’ ); $this->assertNull( $album->id, ’"id" should have defaulted to null’ ); $this->assertNull( $album->title, ’"title" should have defaulted to null’ ); 62 63 64 65 66 67 68 69 70 71 } 72 73 74 75 76 77 78 public function testGetArrayCopyReturnsAnArrayWithPropertyValues() { $album = new Album(); $data = array(’artist’ => ’some artist’, ’id’ => 123, ’title’ => ’some title’); 79 80 81 $album->exchangeArray($data); $copyArray = $album->getArrayCopy(); 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 $this->assertSame( $data[’artist’], $copyArray[’artist’], ’"artist" was not set correctly’ ); $this->assertSame( $data[’id’], $copyArray[’id’], ’"id" was not set correctly’ ); $this->assertSame( $data[’title’], $copyArray[’title’], ’"title" was not set correctly’ ); 14.7. Testing model entities 63
  • 102. Zend Framework 2 Documentation, Release 2.2.5 } 98 99 public function testInputFiltersAreSetCorrectly() { $album = new Album(); 100 101 102 103 $inputFilter = $album->getInputFilter(); 104 105 $this->assertSame(3, $inputFilter->count()); $this->assertTrue($inputFilter->has(’artist’)); $this->assertTrue($inputFilter->has(’id’)); $this->assertTrue($inputFilter->has(’title’)); 106 107 108 109 } 110 111 } We are testing for 5 things: 1. Are all of the Album’s properties initially set to NULL? 2. Will the Album’s properties be set correctly when we call exchangeArray()? 3. Will a default value of NULL be used for properties whose keys are not present in the $data array? 4. Can we get an array copy of our model? 5. Do all elements have input filters present? If we run phpunit again, we will get the following output, confirming that our model is indeed correct: PHPUnit 3.7.13 by Sebastian Bergmann. Configuration read from /var/www/zf2-tutorial/module/Album/test/phpunit.xml ....... Time: 0 seconds, Memory: 11.00Mb OK (7 tests, 25 assertions) 14.8 Testing model tables The final step in this unit testing tutorial for Zend Framework 2 applications is writing tests for our model tables. This test assures that we can get a list of albums, or one album by it’s ID, and that we can save and delete albums from the database. To avoid actual interaction with the database itself, we will replace certain parts with mocks. Create a file AlbumTableTest.php in module/Album/test/AlbumTest/Model with the following contents: <?php namespace AlbumTestModel; use use use use 64 AlbumModelAlbumTable; AlbumModelAlbum; ZendDbResultSetResultSet; PHPUnit_Framework_TestCase; Chapter 14. Unit Testing a Zend Framework 2 application
  • 103. Zend Framework 2 Documentation, Release 2.2.5 class AlbumTableTest extends PHPUnit_Framework_TestCase { public function testFetchAllReturnsAllAlbums() { $resultSet = new ResultSet(); $mockTableGateway = $this->getMock( ’ZendDbTableGatewayTableGateway’, array(’select’), array(), ’’, false ); $mockTableGateway->expects($this->once()) ->method(’select’) ->with() ->will($this->returnValue($resultSet)); $albumTable = new AlbumTable($mockTableGateway); $this->assertSame($resultSet, $albumTable->fetchAll()); } } Since we are testing the AlbumTable here and not the TableGateway class (which has already been tested in Zend Framework), we just want to make sure that our AlbumTable class is interacting with the TableGateway class the way that we expect it to. Above, we’re testing to see if the fetchAll() method of AlbumTable will call the select() method of the $tableGateway property with no parameters. If it does, it should return a ResultSet object. Finally, we expect that this same ResultSet object will be returned to the calling method. This test should run fine, so now we can add the rest of the test methods: public function testCanRetrieveAnAlbumByItsId() { $album = new Album(); $album->exchangeArray(array(’id’ => 123, ’artist’ => ’The Military Wives’, ’title’ => ’In My Dreams’)); $resultSet = new ResultSet(); $resultSet->setArrayObjectPrototype(new Album()); $resultSet->initialize(array($album)); $mockTableGateway = $this->getMock( ’ZendDbTableGatewayTableGateway’, array(’select’), array(), ’’, false ); $mockTableGateway->expects($this->once()) ->method(’select’) ->with(array(’id’ => 123)) ->will($this->returnValue($resultSet)); $albumTable = new AlbumTable($mockTableGateway); $this->assertSame($album, $albumTable->getAlbum(123)); } 14.8. Testing model tables 65
  • 104. Zend Framework 2 Documentation, Release 2.2.5 public function testCanDeleteAnAlbumByItsId() { $mockTableGateway = $this->getMock( ’ZendDbTableGatewayTableGateway’, array(’delete’), array(), ’’, false ); $mockTableGateway->expects($this->once()) ->method(’delete’) ->with(array(’id’ => 123)); $albumTable = new AlbumTable($mockTableGateway); $albumTable->deleteAlbum(123); } public function testSaveAlbumWillInsertNewAlbumsIfTheyDontAlreadyHaveAnId() { $albumData = array( ’artist’ => ’The Military Wives’, ’title’ => ’In My Dreams’ ); $album = new Album(); $album->exchangeArray($albumData); $mockTableGateway = $this->getMock( ’ZendDbTableGatewayTableGateway’, array(’insert’), array(), ’’, false ); $mockTableGateway->expects($this->once()) ->method(’insert’) ->with($albumData); $albumTable = new AlbumTable($mockTableGateway); $albumTable->saveAlbum($album); } public function testSaveAlbumWillUpdateExistingAlbumsIfTheyAlreadyHaveAnId() { $albumData = array( ’id’ => 123, ’artist’ => ’The Military Wives’, ’title’ => ’In My Dreams’, ); $album = new Album(); $album->exchangeArray($albumData); $resultSet = new ResultSet(); $resultSet->setArrayObjectPrototype(new Album()); $resultSet->initialize(array($album)); $mockTableGateway = $this->getMock( ’ZendDbTableGatewayTableGateway’, array(’select’, ’update’), 66 Chapter 14. Unit Testing a Zend Framework 2 application
  • 105. Zend Framework 2 Documentation, Release 2.2.5 array(), ’’, false ); $mockTableGateway->expects($this->once()) ->method(’select’) ->with(array(’id’ => 123)) ->will($this->returnValue($resultSet)); $mockTableGateway->expects($this->once()) ->method(’update’) ->with( array( ’artist’ => ’The Military Wives’, ’title’ => ’In My Dreams’ ), array(’id’ => 123) ); $albumTable = new AlbumTable($mockTableGateway); $albumTable->saveAlbum($album); } public function testExceptionIsThrownWhenGettingNonExistentAlbum() { $resultSet = new ResultSet(); $resultSet->setArrayObjectPrototype(new Album()); $resultSet->initialize(array()); $mockTableGateway = $this->getMock( ’ZendDbTableGatewayTableGateway’, array(’select’), array(), ’’, false ); $mockTableGateway->expects($this->once()) ->method(’select’) ->with(array(’id’ => 123)) ->will($this->returnValue($resultSet)); $albumTable = new AlbumTable($mockTableGateway); try { $albumTable->getAlbum(123); } catch (Exception $e) { $this->assertSame(’Could not find row 123’, $e->getMessage()); return; } $this->fail(’Expected exception was not thrown’); } These tests are nothing complicated and they should be self explanatory. In each test we are injecting a mock table gateway into our AlbumTable and set our expectations accordingly. We are testing that: 1. We can retrieve an individual album by its ID. 14.8. Testing model tables 67
  • 106. Zend Framework 2 Documentation, Release 2.2.5 2. We can delete albums. 3. We can save new album. 4. We can update existing albums. 5. We will encounter an exception if we’re trying to retrieve an album that doesn’t exist. Running phpunit command for one last time, we get the output as follows: PHPUnit 3.7.13 by Sebastian Bergmann. Configuration read from /var/www/zf2-tutorial/module/Album/test/phpunit.xml ............. Time: 0 seconds, Memory: 11.50Mb OK (13 tests, 34 assertions) 14.9 Conclusion In this short tutorial we gave a few examples how different parts of a Zend Framework 2 MVC application can be tested. We covered setting up the environment for testing, how to test controllers and actions, how to approach failing test cases, how to configure the service manager, as well as how to test model entities and model tables. This tutorial is by no means a definitive guide to writing unit tests, just a small stepping stone helping you develop applications of higher quality. 68 Chapter 14. Unit Testing a Zend Framework 2 application
  • 107. CHAPTER 15 Using the EventManager This tutorial explores the various features of ZendEventManager. 15.1 Terminology • An Event is a named action. • A Listener is any PHP callback that reacts to an event. • An EventManager aggregates listeners for one or more named events, and triggers events. Typically, an event will be modeled as an object, containing metadata surrounding when and how it was triggered, including the event name, what object triggered the event (the “target”), and what parameters were provided. Events are named, which allows a single listener to branch logic based on the event. 15.2 Getting started The minimal things necessary to start using events are: • An EventManager instance • One or more listeners on one or more events • A call to trigger() an event The simplest example looks something like this: 1 use ZendEventManagerEventManager; 2 3 4 5 6 7 8 9 10 11 12 $events = new EventManager(); $events->attach(’do’, function ($e) { $event = $e->getName(); $params = $e->getParams(); printf( ’Handled event "%s", with parameters %s’, $event, json_encode($params) ); }); 69
  • 108. Zend Framework 2 Documentation, Release 2.2.5 13 14 15 $params = array(’foo’ => ’bar’, ’baz’ => ’bat’); $events->trigger(’do’, null, $params); The above will result in the following: Handled event "do", with parameters {"foo":"bar","baz":"bat"} Note: Throughout this tutorial, we use closures as listeners. However, any valid PHP callback can be attached as a listeners: PHP function names, static class methods, object instance methods, functors, or closures. We use closures within this post simply for illustration and simplicity. If you were paying attention to the example, you will have noted the null argument. Why is it there? Typically, you will compose an EventManager within a class, to allow triggering actions within methods. The middle argument to trigger() is the “target”, and in the case described, would be the current object instance. This gives event listeners access to the calling object, which can often be useful. 1 2 3 use ZendEventManagerEventManager; use ZendEventManagerEventManagerAwareInterface; use ZendEventManagerEventManagerInterface; 4 5 6 7 class Example implements EventManagerAwareInterface { protected $events; 8 public function setEventManager(EventManagerInterface $events) { $events->setIdentifiers(array( __CLASS__, get_class($this) )); $this->events = $events; } 9 10 11 12 13 14 15 16 17 public function getEventManager() { if (!$this->events) { $this->setEventManager(new EventManager()); } return $this->events; } 18 19 20 21 22 23 24 25 public function do($foo, $baz) { $params = compact(’foo’, ’baz’); $this->getEventManager()->trigger(__FUNCTION__, $this, $params); } 26 27 28 29 30 31 32 } 33 34 $example = new Example(); 35 36 37 38 39 40 $example->getEventManager()->attach(’do’, function($e) { $event = $e->getName(); $target = get_class($e->getTarget()); // "Example" $params = $e->getParams(); printf( 70 Chapter 15. Using the EventManager
  • 109. Zend Framework 2 Documentation, Release 2.2.5 ’Handled event "%s" on target "%s", with parameters %s’, $event, $target, json_encode($params) 41 42 43 44 ); 45 46 }); 47 48 $example->do(’bar’, ’bat’); The above is basically the same as the first example. The main difference is that we’re now using that middle argument in order to pass the target, the instance of Example, on to the listeners. Our listener is now retrieving that ($e->getTarget()), and doing something with it. If you’re reading this critically, you should have a new question: What is the call to setIdentifiers() for? 15.3 Shared managers One aspect that the EventManager implementation SharedEventManagerInterface implementation. provides is an ability to compose a ZendEventManagerSharedEventManagerInterface describes an object that aggregates listeners for events attached to objects with specific identifiers. It does not trigger events itself. Instead, an EventManager instance that composes a SharedEventManager will query the SharedEventManager for listeners on identifiers it’s interested in, and trigger those listeners as well. How does this work, exactly? Consider the following: 1 use ZendEventManagerSharedEventManager; 2 3 4 5 6 7 8 9 10 11 12 13 14 $sharedEvents = new SharedEventManager(); $sharedEvents->attach(’Example’, ’do’, function ($e) { $event = $e->getName(); $target = get_class($e->getTarget()); // "Example" $params = $e->getParams(); printf( ’Handled event "%s" on target "%s", with parameters %s’, $event, $target, json_encode($params) ); }); This looks almost identical to the previous example; the key difference is that there is an additional argument at the start of the list, ’Example’. This code is basically saying, “Listen to the ‘do’ event of the ‘Example’ target, and, when notified, execute this callback.” This is where the setIdentifiers() argument of EventManager comes into play. The method allows passing a string, or an array of strings, defining the name or names of the context or targets the given instance will be interested in. If an array is given, then any listener on any of the targets given will be notified. So, getting back to our example, let’s assume that the above shared listener is registered, and also that the Example class is defined as above. We can then execute the following: 1 2 3 $example = new Example(); $example->getEventManager()->setSharedManager($sharedEvents); $example->do(’bar’, ’bat’); 15.3. Shared managers 71
  • 110. Zend Framework 2 Documentation, Release 2.2.5 and expect the following to be echo‘d: Handled event "do" on target "Example", with parameters {"foo":"bar","baz":"bat"} Now, let’s say we extended Example as follows: 1 2 3 class SubExample extends Example { } One interesting aspect of our setEventManager() method is that we defined it to listen both on __CLASS__ and get_class($this). This means that calling do() on our SubExample class would also trigger the shared listener! It also means that, if desired, we could attach to specifically SubExample, and listeners attached to only the Example target would not be triggered. Finally, the names used as contexts or targets need not be class names; they can be some name that only has meaning in your application if desired. As an example, you could have a set of classes that respond to “log” or “cache” – and listeners on these would be notified by any of them. Note: We recommend using class names, interface names, and/or abstract class names for identifiers. This makes determining what events are available easier, as well as finding which listeners might be attaching to those events. Interfaces make a particularly good use case, as they allow attaching to a group of related classes a single operation. At any point, if you do not want to notify shared listeners, pass a null value to setSharedManager(): $events->setSharedManager(null); and they will be ignored. If at any point, you want to enable them again, pass the SharedEventManager instance: $events->setSharedManager($sharedEvents); 72 Chapter 15. Using the EventManager
  • 111. CHAPTER 16 Wildcards So far, with both a normal EventManager instance and with the SharedEventManager instance, we’ve seen the usage of singular strings representing the event and target names to which we want to attach. What if you want to attach a listener to multiple events or targets? The answer is to supply an array of events or targets, or a wildcard, *. Consider the following examples: 1 2 3 4 5 // Multiple named events: $events->attach( array(’foo’, ’bar’, ’baz’), // events $listener ); 6 7 8 9 10 11 // All events via wildcard: $events->attach( ’*’, // all events $listener ); 12 13 14 15 16 17 18 // Multiple named targets: $sharedEvents->attach( array(’Foo’, ’Bar’, ’Baz’), // targets ’doSomething’, // named event $listener ); 19 20 21 22 23 24 25 // All targets via wildcard $sharedEvents->attach( ’*’, // all targets ’doSomething’, // named event $listener ); 26 27 28 29 30 31 32 // Mix and match: multiple named events on multiple named targets: $sharedEvents->attach( array(’Foo’, ’Bar’, ’Baz’), // targets array(’foo’, ’bar’, ’baz’), // events $listener ); 73
  • 112. Zend Framework 2 Documentation, Release 2.2.5 33 34 35 36 37 38 39 // Mix and match: all events on multiple named targets: $sharedEvents->attach( array(’Foo’, ’Bar’, ’Baz’), // targets ’*’, // events $listener ); 40 41 42 43 44 45 46 // Mix and match: multiple named events on all targets: $sharedEvents->attach( ’*’, // targets array(’foo’, ’bar’, ’baz’), // events $listener ); 47 48 49 50 51 52 53 // Mix and match: all events on all targets: $sharedEvents->attach( ’*’, // targets ’*’, // events $listener ); The ability to specify multiple targets and/or events when attaching can slim down your code immensely. 74 Chapter 16. Wildcards
  • 113. CHAPTER 17 Listener aggregates Another approach to listening to multiple events is via a concept of listener aggregates, represented by ZendEventManagerListenerAggregateInterface. Via this approach, a single class can listen to multiple events, attaching one or more instance methods as listeners. This interface defines two methods, attach(EventManagerInterface $events) and detach(EventManagerInterface $events). Basically, you pass an EventManager instance to one and/or the other, and then it’s up to the implementing class to determine what to do. As an example: 1 2 3 4 use use use use ZendEventManagerEventInterface; ZendEventManagerEventManagerInterface; ZendEventManagerListenerAggregateInterface; ZendLogLogger; 5 6 7 8 9 class LogEvents implements ListenerAggregateInterface { protected $listeners = array(); protected $log; 10 11 12 13 14 public function __construct(Logger $log) { $this->log = $log; } 15 16 17 18 19 20 public function attach(EventManagerInterface $events) { $this->listeners[] = $events->attach(’do’, array($this, ’log’)); $this->listeners[] = $events->attach(’doSomethingElse’, array($this, ’log’)); } 21 22 23 24 25 26 27 28 29 public function detach(EventCollection $events) { foreach ($this->listeners as $index => $listener) { if ($events->detach($listener)) { unset($this->listeners[$index]; } } } 30 75
  • 114. Zend Framework 2 Documentation, Release 2.2.5 public function log(EventInterface $e) { $event = $e->getName(); $params = $e->getParams(); $this->log->info(sprintf(’%s: %s’, $event, json_encode($params))); } 31 32 33 34 35 36 37 } You can attach this using either attach() or attachAggregate(): $logListener = new LogEvents($logger); $events->attachAggregate($logListener); // OR $events->attach($logListener); Any events the aggregate attaches to will then be notified when triggered. Why bother? For a couple of reasons: • Aggregates allow you to have stateful listeners. The above example demonstrates this via the composition of the logger; another example would be tracking configuration options. • Aggregates make detaching listeners easier. When you call attach() normally, you receive a ZendStdlibCallbackHandler instance; the only way to detach() a listener is to pass that instance back – which means if you want to detach later, you need to keep that instance somewhare. Aggregates typically do this for you – as you can see in the example above. 17.1 Introspecting results Sometimes you’ll want to know what your listeners returned. One thing to remember is that you may have multiple listeners on the same event; the interface for results must be consistent regardless of the number of listeners. The EventManager implementation by default returns a ZendEventManagerResponseCollection instance. This class extends PHP’s SplStack, allowing you to loop through responses in reverse order (since the last one executed is likely the one you’re most interested in). It also implements the following methods: • first() will retrieve the first result received • last() will retrieve the last result received • contains($value) allows you to test all values to see if a given one was received, and returns simply a boolean true if found, and false if not. Typically, you should not worry about the return values from events, as the object triggering the event shouldn’t really have much insight into what listeners are attached. However, sometimes you may want to short-circuit execution if interesting results are obtained. 17.2 Short-ciruiting listener execution You may want to short-ciruit execution if a particular result is obtained, or if a listener determines that something is wrong, or that it can return something quicker than the target. As examples, one rationale for adding an EventManager is as a caching mechanism. You can trigger one event early in the method, returning if a cache is found, and trigger another event late in the method, seeding the cache. The EventManager component offers two ways to handle this. The first is to pass a callback as the last argument to trigger(); if that callback returns a boolean true, execution is halted. 76 Chapter 17. Listener aggregates
  • 115. Zend Framework 2 Documentation, Release 2.2.5 Here’s an example: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public function someExpensiveCall($criteria1, $criteria2) { $params = compact(’criteria1’, ’criteria2’); $results = $this->getEventManager()->trigger( __FUNCTION__, $this, $params, function ($r) { return ($r instanceof SomeResultClass); } ); if ($results->stopped()) { return $results->last(); } 15 // ... do some work ... 16 17 } With this paradigm, we know that the likely reason of execution halting is due to the last result meeting the test callback criteria; as such, we simply return that last result. The other way to halt execution is within a listener, acting on the Event object it receives. In this case, the listener calls stopPropagation(true), and the EventManager will then return without notifying any additional listeners. 1 2 3 4 $events->attach(’do’, function ($e) { $e->stopPropagation(); return new SomeResultClass(); }); This, of course, raises some ambiguity when using the trigger paradigm, as you can no longer be certain that the last result meets the criteria it’s searching on. As such, we recommend that you standardize on one approach or the other. 17.3 Keeping it in order On occasion, you may be concerned about the order in which listeners execute. As an example, you may want to do any logging early, to ensure that if short-circuiting occurs, you’ve logged; or if implementing a cache, you may want to return early if a cache hit is found, and execute late when saving to a cache. Each of EventManager::attach() and SharedEentManager::attach() accept one additional argument, a priority. By default, if this is omitted, listeners get a priority of 1, and are executed in the order in which they are attached. However, if you provide a priority value, you can influence order of execution. • Higher priority values execute earlier. • Lower (negative) priority values execute later. To borrow an example from earlier: 1 2 3 4 5 6 7 8 $priority = 100; $events->attach(’Example’, ’do’, function($e) { $event = $e->getName(); $target = get_class($e->getTarget()); // "Example" $params = $e->getParams(); printf( ’Handled event "%s" on target "%s", with parameters %s’, $event, 17.3. Keeping it in order 77
  • 116. Zend Framework 2 Documentation, Release 2.2.5 $target, json_encode($params) 9 10 11 12 ); }, $priority); This would execute with high priority, meaning it would execute early. If we changed $priority to -100, it would execute with low priority, executing late. While you can’t necessarily know all the listeners attached, chances are you can make adequate guesses when necessary in order to set appropriate priority values. We advise avoiding setting a priority value unless absolutely necessary. 17.4 Custom event objects Hopefully some of you have been wondering, “where and when is the Event object created”? In all of the examples above, it’s created based on the arguments passed to trigger() – the event name, target, and parameters. Sometimes, however, you may want greater control over the object. As an example, one thing that looks like a code smell is when you have code like this: 1 2 3 4 $routeMatch = $e->getParam(’route-match’, false); if (!$routeMatch) { // Oh noes! we cannot do our work! whatever shall we do?!?!?! } The problems with this are several. First, relying on string keys is going to very quickly run into problems – typos when setting or retrieving the argument can lead to hard to debug situations. Second, we now have a documentation issue; how do we document expected arguments? how do we document what we’re shoving into the event? Third, as a side effect, we can’t use IDE or editor hinting support – string keys give these tools nothing to work with. Similarly, consider how you might represent a computational result of a method when triggering an event. As an example: 1 2 3 // in the method: $params[’__RESULT’] = $computedResult; $events->trigger(__FUNCTION__ . ’.post’, $this, $params); 4 5 6 7 8 9 // in the listener: $result = $e->getParam(’__RESULT__’); if (!$result) { // Oh noes! we cannot do our work! whatever shall we do?!?!?! } Sure, that key may be unique, but it suffers from a lot of the same issues. So, the solution is to create custom events. As an example, we have a custom MvcEvent in the ZF2 MVC layer. This event composes the application instance, the router, the route match object, request and response objects, the view model, and also a result. We end up with code like this in our listeners: 1 2 3 4 5 6 $response = $e->getResponse(); $result = $e->getResult(); if (is_string($result)) { $content = $view->render(’layout.phtml’, array(’content’ => $result)); $response->setContent($content); } But how do we use this custom event? Simple: trigger() can accept an event object instead of any of the event name, target, or params arguments. 78 Chapter 17. Listener aggregates
  • 117. Zend Framework 2 Documentation, Release 2.2.5 1 2 $event = new CustomEvent(); $event->setSomeKey($value); 3 4 5 // Injected with event name and target: $events->trigger(’foo’, $this, $event); 6 7 8 9 // Injected with event name: $event->setTarget($this); $events->trigger(’foo’, $event); 10 11 12 13 14 // Fully encapsulates all necessary properties: $event->setName(’foo’); $event->setTarget($this); $events->trigger($event); 15 16 17 18 // Passing a callback following the event object works for // short-circuiting, too. $results = $events->trigger(’foo’, $this, $event, $callback); This is a really powerful technique for domain-specific event systems, and definitely worth experimenting with. 17.5 Putting it together: Implementing a simple caching system In previous sections, I indicated that short-circuiting is a way to potentially implement a caching solution. Let’s create a full example. First, let’s define a method that could use caching. You’ll note that in most of the examples, I’ve used __FUNCTION__ as the event name; this is a good practice, as it makes it simple to create a macro for triggering events, as well as helps to keep event names unique (as they’re usually within the context of the triggering class). However, in the case of a caching example, this would lead to identical events being triggered. As such, I recommend postfixing the event name with semantic names: “do.pre”, “do.post”, “do.error”, etc. I’ll use that convention in this example. Additionally, you’ll notice that the $params I pass to the event is usually the list of parameters passed to the method. This is because those are often not stored in the object, and also to ensure the listeners have the exact same context as the calling method. But it raises an interesting problem in this example: what name do we give the result of the method? One standard that has emerged is the use of __RESULT__, as double-underscored variables are typically reserved for the sytem. Here’s what the method will look like: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public function someExpensiveCall($criteria1, $criteria2) { $params = compact(’criteria1’, ’criteria2’); $results = $this->getEventManager()->trigger( __FUNCTION__ . ’.pre’, $this, $params, function ($r) { return ($r instanceof SomeResultClass); } ); if ($results->stopped()) { return $results->last(); } 15 16 // ... do some work ... 17.5. Putting it together: Implementing a simple caching system 79
  • 118. Zend Framework 2 Documentation, Release 2.2.5 17 $params[’__RESULT__’] = $calculatedResult; $this->events()->trigger(__FUNCTION__ . ’.post’, $this, $params); return $calculatedResult; 18 19 20 21 } Now, to provide some caching listeners. We’ll need to attach to each of the “someExpensiveCall.pre” and “someExpensiveCall.post” methods. In the former case, if a cache hit is detected, we return it, and move on. In the latter, we store the value in the cache. We’ll assume $cache is defined, and follows the paradigms of ZendCache. We’ll want to return early if a hit is detected, and execute late when saving a cache (in case the result is modified by another listener). As such, we’ll set the “someExpensiveCall.pre” listener to execute with priority 100, and the “someExpensiveCall.post” listener to execute with priority -100. 1 2 3 4 5 6 $events->attach(’someExpensiveCall.pre’, function($e) use ($cache) { $params = $e->getParams(); $key = md5(json_encode($params)); $hit = $cache->load($key); return $hit; }, 100); 7 8 9 10 11 12 13 14 $events->attach(’someExpensiveCall.post’, function($e) use ($cache) { $params = $e->getParams(); $result = $params[’__RESULT__’]; unset($params[’__RESULT__’]); $key = md5(json_encode($params)); $cache->save($result, $key); }, -100); Note: The above could have been done within a ListenerAggregate, which would have allowed keeping the $cache instance as a stateful property, instead of importing it into closures. Another approach would be to move the body of the method to a listener as well, which would allow using the priority system in order to implement caching. That would look like this: 1 2 3 4 5 6 public function setEventManager(EventManagerInterface $events) { $this->events = $events; $events->setIdentifiers(array(__CLASS__, get_class($this))); $events->attach(’someExpensiveCall’, array($this, ’doSomeExpensiveCall’)); } 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public function someExpensiveCall($criteria1, $criteria2) { $params = compact(’criteria1’, ’criteria2’); $results = $this->getEventManager()->trigger( __FUNCTION__, $this, $params, function ($r) { return ($r instanceof SomeResultClass); } ); return $results->last(); } 21 80 Chapter 17. Listener aggregates
  • 119. Zend Framework 2 Documentation, Release 2.2.5 22 23 24 25 26 27 public function doSomeExpensiveCall($e) { // ... do some work ... $e->setParam(’__RESULT__’, $calculatedResult); return $calculatedResult; } The listeners would then attach to the “someExpensiveCall” event, with the cache lookup listener listening at high priority, and the cache storage listener listening at low (negative) priority. Sure, we could probably simply add caching to the object itself - but this approach allows the same handlers to be attached to multiple events, or to attach multiple listeners to the same events (e.g. an argument validator, a logger and a cache manager). The point is that if you design your object with events in mind, you can easily make it more flexible and extensible, without requiring developers to actually extend it – they can simply attach listeners. 17.6 Conclusion The EventManager is a powerful component. It drives the workflow of the MVC layer, and is used in countless components to provide hook points for developers to manipulate the workflow. It can be put to any number of uses inside your own code, and is an important part of your Zend Framework toolbox. 17.6. Conclusion 81
  • 120. Zend Framework 2 Documentation, Release 2.2.5 82 Chapter 17. Listener aggregates
  • 121. CHAPTER 18 Advanced Configuration Tricks Configuration of Zend Framework 2 applications happens in several steps: • Initial configuration is passed to the Application instance and used to seed the ModuleManager and ServiceManager. In this tutorial, we will call this configuration system configuration. • The ModuleManager‘s ConfigListener aggregates configuration and merges it while modules are being loaded. In this tutorial, we will call this configuration application configuration. • Once configuration is aggregated from all modules, the ConfigListener will also merge application configuration globbed in specified directories (typically config/autoload/). In this tutorial, we’ll look at the exact sequence, and how you can tie into it. 18.1 System configuration To begin module loading, we have to tell the Application instance about the available modules and where they live, optionally provide some information to the default module listeners (e.g., where application configuration lives, and what files to load; whether to cache merged configuration, and where; etc.), and optionally seed the ServiceManager. For purposes of this tutorial we will call this the system configuration. When using the skeleton application, the system configuration config/application.config.php. The defaults look like this: 1 2 3 4 5 6 is by default in <?php return array( // This should be an array of module namespaces used in the application. ’modules’ => array( ’Application’, ), 7 8 9 10 11 12 13 14 15 16 // 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’, 83
  • 122. Zend Framework 2 Documentation, Release 2.2.5 ), 17 18 // An array of paths from which to glob configuration files after // modules are loaded. These effectively overide configuration // provided by modules themselves. Paths may use GLOB_BRACE notation. ’config_glob_paths’ => array( ’config/autoload/{,*.}{global,local}.php’, ), 19 20 21 22 23 24 25 // Whether or not to enable a configuration cache. // If enabled, the merged configuration will be cached and used in // subsequent requests. //’config_cache_enabled’ => $booleanValue, 26 27 28 29 30 // The key used to create the configuration cache file name. //’config_cache_key’ => $stringKey, 31 32 33 // Whether or not to enable a module class map cache. // If enabled, creates a module class map cache which will be used // by in future requests, to reduce the autoloading process. //’module_map_cache_enabled’ => $booleanValue, 34 35 36 37 38 // The key used to create the class map cache file name. //’module_map_cache_key’ => $stringKey, 39 40 41 // The path in which to cache merged configuration. //’cache_dir’ => $stringPath, 42 43 44 // // // // 45 46 47 48 Whether or not to enable modules dependency checking. Enabled by default, prevents usage of modules that depend on other modules that weren’t loaded. ’check_dependencies’ => true, ), 49 50 // Used to create an own service manager. May contain one or more child arrays. //’service_listener_options’ => array( // array( // ’service_manager’ => $stringServiceManagerName, // ’config_key’ => $stringConfigKey, // ’interface’ => $stringOptionalInterface, // ’method’ => $stringRequiredMethodName, // ), // ) 51 52 53 54 55 56 57 58 59 60 // Initial configuration with which to seed the ServiceManager. // Should be compatible with ZendServiceManagerConfig. // ’service_manager’ => array(), 61 62 63 64 ); The system configuration is for the bits and pieces related to the MVC that run before your application is ready. The configuration is usually brief, and quite minimal. Also, system configuration is used immediately, and is not merged with any other configuration – which means it cannot be overridden by a module. This leads us to our first trick: how do you provide environment-specific system configuration? 84 Chapter 18. Advanced Configuration Tricks
  • 123. Zend Framework 2 Documentation, Release 2.2.5 18.1.1 Environment-specific system configuration What happens when you want to change the set of modules you use based on the environment? Or if the configuration caching should be enabled based on environment? It is for this reason that the default system configuration we provide in the skeleton application is in PHP; providing it in PHP means you can programmatically manipulate it. As an example, let’s make the following requirements: • We want to use the ZendDeveloperTools module in development only. • We want to have configuration caching on in production only. To make this happen, we’ll set an environment variable in our web server configuration, APP_ENV. In Apache, you’d put a directive like the following in either your system-wide apache.conf or httpd.conf, or in the definition for your virtual host; alternately, it can be placed in an .htaccess file. SetEnv "APP_ENV" "development" For other web servers, consult the web server documentation to determine how to set environment variables. To simplify matters, we’ll assume the environment is “production” if no environment variable is present. We’ll modify the config/application.config.php file to read as follows: 1 2 <?php $env = getenv(’APP_ENV’) ?: ’production’; 3 4 5 6 7 8 9 10 // Use the $env value to determine which modules to load $modules = array( ’Application’, ); if ($env == ’development’) { $modules[] = ’ZendDeveloperTools’; } 11 12 13 return array( ’modules’ => $modules, 14 15 16 17 18 19 ’module_listener_options’ => array( ’module_paths’ => array( ’./module’, ’./vendor’, ), 20 21 22 23 ’config_glob_paths’ => array( ’config/autoload/{,*.}{global,local}.php’, ), 24 25 26 // Use the $env value to determine the state of the flag ’config_cache_enabled’ => ($env == ’production’), 27 28 ’config_cache_key’ => ’app_config’, 29 30 31 // Use the $env value to determine the state of the flag ’module_map_cache_enabled’ => ($env == ’production’), 32 33 ’module_map_cache_key’ => ’module_map’, 34 35 ’cache_dir’ => ’data/config/’, 18.1. System configuration 85
  • 124. Zend Framework 2 Documentation, Release 2.2.5 36 // Use the $env value to determine the state of the flag ’check_dependencies’ => ($env != ’production’), 37 38 ), 39 40 ); This approach gives you flexibility to alter system-level settings. However, how about altering application specific settings (not system configuration) based on the environment? 18.1.2 Environment-specific application configuration Sometimes you want to change application configuration to load things such as database adapters, log writers, cache adapters, and more based on the environment. These are typically managed in the service manager, and may be defined by modules. You can override them at the application level via ZendModuleManagerListenerConfigListener, by specifying a glob path in the system configuration – the module_listener_options.config_glob_paths key from the previous examples. The default value for this is config/autoload/{,*.}{global,local}.php. What this means is that it will look for application configuration files in the config/autoload directory, in the following order: • global.php • *.global.php • local.php • *.local.php This allows you to define application-level defaults in “global” configuration files, which you would then commit to your version control system, and environment-specific overrides in your “local” configuration files, which you would omit from version control. This is a great solution for development, as it allows you to specify alternate configuration that’s specific to your development environment without worrying about accidently deploying it. However, what if you have more environments – such as a “testing” or “staging” environment – and they each have their own specific overrides? Again, the application environment variable comes to play. We can alter the glob path in the system configuration slightly: ’config_glob_paths’ => array( sprintf(’config/autoload/{,*.}{global,%s,local}.php’, $env) ), The above will allow you to define an additional set of application configuration files per environment; furthermore, these will be loaded only if that environment is detected! As an example, consider the following tree of configuration files: config/ autoload/ global.php local.php users.development.php users.testing.php users.local.php If $env evaluates to testing, then the following files will be merged, in the following order: 86 Chapter 18. Advanced Configuration Tricks
  • 125. Zend Framework 2 Documentation, Release 2.2.5 global.php users.testing.php local.php users.local.php Note that users.development.php is not loaded – this is because it will not match the glob pattern! Also, because of the order in which they are loaded, you can predict which values will overwrite the others, allowing you to both selectively overwrite as well as debug later. Note: The files under config/autoload/ are merged after your module configuration, detailed in next section. We have detailed it here, however, as setting up the application configuration glob path happens within the system configuration (config/application.config.php). 18.2 Module Configuration One responsibility of modules is to provide their own configuration to the application. Modules have two general mechanisms for doing this. First, modules that either implement ZendModuleManagerFeatureConfigProviderInterface and/or a getConfig() method can return their configuration. The default, recommended implementation of the getConfig() method is: public function getConfig() { return include __DIR__ . ’/config/module.config.php’; } where module.config.php returns a PHP array. From that PHP array you can provide general configuration as well as configuration for all the available Manager classes provided by the ServiceManager. Please refer to the Configuration mapping table to see which configuration key is used for each specific Manager. Second, modules can implement a number of interfaces and/or methods related to specific service manager or plugin manager configuration. You will find an overview of all interfaces and their matching Module Configuration functions inside the Configuration mapping table. All interfaces are in the ZendModuleManagerFeature namespace, and each is expected to return an array of configuration for a service manager, as denoted in the section on default service configuration. 18.2. Module Configuration 87
  • 126. Zend Framework 2 Documentation, Release 2.2.5 18.3 Configuration mapping table Manager name Interface name Module Method name Config key name ControllerPluginManager ControllerPluginProviderInterface getControllerPluginConfig() controller_plugins ControllerLoader ControllerProviderInterface getControllerConfig() controllers FilterManager FilterProviderInterface getFilterConfig() filters FormElementManager FormElementProviderInterface getFormElementConfig() form_elements HydratorManager HydratorProviderInterfacegetHydratorConfig() hydrators InputFilterManager InputFilterProviderInterface getInputFilterConfig() input_filters RoutePluginManager RouteProviderInterface getRouteConfig() route_manager SerializerAdapterManager SerializerProviderInterface getSerializerConfig() serializers ServiceLocator ServiceProviderInterface getServiceConfig() service_manager ValidatorManager ValidatorProviderInterface getValidatorConfig() validators ViewHelperManager ViewHelperProviderInterface getViewHelperConfig() view_helpers 18.4 Configuration Priority Considering that you may have service configuration in your module configuration file, what has precedence? The order in which they are merged is: • configuration returned by getConfig() • configuration returned by the various service configuration methods in a module class In other words, your various service configuration methods win. Additionally, and of particular note: the configuration returned from those methods will not be cached. The reason for this is that it is not uncommon to use closures or factory instances in configuration returned from your Module class – which cannot be cached reliably. Note: Use the various service configuration methods when you need to define closures or instance callbacks for factories, abstract factories, and initializers. This prevents caching problems, and also allows you to write your configuration files in other markup formats. 18.5 Configuration merging workflow To cap off the tutorial, let’s review how and when configuration is defined and merged. • System configuration – Defined in config/application.config.php – No merging occurs – Allows manipulation programmatically, which allows the ability to: * Alter flags based on computed values * Alter the configuration glob path based on computed values – Configuration is passed to the Application instance, and then the ModuleManager in order to initialize the system. • Application configuration – The ModuleManager loops through each module class in the order defined in the system configuration 88 Chapter 18. Advanced Configuration Tricks
  • 127. Zend Framework 2 Documentation, Release 2.2.5 * Service configuration defined in Module class methods is aggregated * Configuration returned by Module::getConfig() is aggregated – Files detected from the service configuration config_glob_paths setting are merged, based on the order they resolve in the glob path. – Merged configuration is finally passed to the ServiceManager 18.5. Configuration merging workflow 89
  • 128. Zend Framework 2 Documentation, Release 2.2.5 90 Chapter 18. Advanced Configuration Tricks
  • 129. CHAPTER 19 Using ZendNavigation in your Album Module In this tutorial we will use the ZendNavigation component to add a navigation menu to the black bar at the top of the screen, and add breadcrumbs above the main site content. 19.1 Preparation In a real world application, the album browser would be only a portion of a working website. Usually the user would land on a homepage first, and be able to view albums by using a standard navigation menu. So that we have a site that is more realistic than just the albums feature, lets make the standard skeleton welcome page our homepage, with the /album route still showing our album module. In order to make this change, we need to undo some work we did earlier. Currently, navigating to the root of your app (/) routes you to the AlbumController‘s default action. Let’s undo this route change so we have two discrete entry points to the app, a home page, and an albums area. module/Application/config/module.config.php 1 2 3 4 5 6 7 8 9 10 ’home’ => array( ’type’ => ’ZendMvcRouterHttpLiteral’, ’options’ => array( ’route’ => ’/’, ’defaults’ => array( ’controller’ => ’ApplicationControllerIndex’, // <-- change back here ’action’ => ’index’, ), ), ), This change means that if you go to the home page of your application (http://zf2-tutorial.localhost/), you see the default skeleton application introduction. Your list of albums is still available at the /album route. 19.2 Setting Up ZendNavigation Firstly, we need to tell our application which NavigationFactory to use when using the bundled navigation view helpers. Thankfully, ZF2 comes with a default factory that will suit our needs just fine. To tell ZF2 to use this default 91
  • 130. Zend Framework 2 Documentation, Release 2.2.5 factory, we simply add a navigation key to the service manager. Its best to do this in the Application module, because, like the translation data, this is specific to the entire application, and not just to our album pages: module/Application/config/module.config.php 1 2 3 4 5 6 ’service_manager’ => array( ’factories’ => array( ’translator’ => ’ZendI18nTranslatorTranslatorServiceFactory’, ’navigation’ => ’ZendNavigationServiceDefaultNavigationFactory’, // <-- add this ), ), 19.3 Configuring our Site Map Next up, we need ZendNavigation to understand the hierarchy of our site. Thankfully, if we add a navigation key to our merged config, the navigation factory will automagically create the container and pages needed to use the view helpers. Let’s do this in the Application module: module/Application/config/module.config.php 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 return array( ... ’navigation’ => array( ’default’ => array( array( ’label’ => ’Home’, ’route’ => ’home’, ), array( ’label’ => ’Album’, ’route’ => ’album’, ’pages’ => array( array( ’label’ => ’Add’, ’route’ => ’album’, ’action’ => ’add’, ), array( ’label’ => ’Edit’, ’route’ => ’album’, ’action’ => ’edit’, ), array( ’label’ => ’Delete’, ’route’ => ’album’, ’action’ => ’delete’, ), ), ), ), ), ... ); This configuration maps out the pages we’ve defined in our controller, with labels linking to the given route names. You can define highly complex hierarchical sites here with pages and sub-pages linking to route names, controller/action pairs or external uris. For more information see the docs here. 92 Chapter 19. Using ZendNavigation in your Album Module
  • 131. Zend Framework 2 Documentation, Release 2.2.5 19.4 Adding the Menu View Helper Now that we have the navigation helper configured by our service manager and merged config, we can easily add the menu to the title bar to our layout by using the menu view helper: module/Application/view/layout/layout.phtml 1 2 3 4 5 6 7 ... <a class="brand" href="<?php echo $this->url(’home’) ?>"><?php echo $this->translate(’Skeleton Application’) ? <?php // <-- Add this !! echo $this->navigation(’navigation’)->menu(); ?> ... The navigation helper is built in to Zend Framework 2, and uses the service manager configuration we’ve already defined to configure itself automatically. Refreshing your application you will see a working menu, with just a few tweaks however, we can make it look awesome: module/Application/view/layout/layout.phtml 1 2 3 4 5 6 7 8 9 <a class="brand" href="<?php echo $this->url(’home’) ?>"><?php echo $this->translate(’Skeleton Application’) ?></a <?php // <-- Update this !! echo $this->navigation(’navigation’) ->menu() ->setMinDepth(0) ->setMaxDepth(0) ->setUlClass(’nav navbar-nav’); ?> Here we tell the renderer to give the root UL the class of ‘nav’ so that Twitter Bootstrap styles the menu correctly, and only render the first level of any given page. If you view your application in your browser, you will now see a nicely styled menu appear in the title bar. The great thing about ZendNavigation is that it integrates with ZF2’s route so can tell which page you are currently viewing. Because of this, it sets the active page to have a class of active in the menu. Twitter Bootstrap uses this to highlight your current page accordingly. 19.5 Adding Breadcrumbs Adding breadcrumbs is initially just as simple. In our layout.phtml we want to add breadcrumbs above the main content pane, so our foolish user knows exactly where they are in our complex website. Inside the container div, before we output the content from the view, let’s add a simple breadcrumb by using the breadcrumbs view helper: module/Application/view/layout/layout.phtml 1 2 3 4 5 6 ... <div class="container"> <?php echo $this->navigation(’navigation’)->breadcrumbs()->setMinDepth(0); // <-- Add this!! ?> <?php echo $this->content; ?> </div> ... This adds a simple but functional breadcrumb to every page (we simply tell it to render from a depth of 0 so we see all level of pages) but we can do better than that! Because Bootstrap has a styled breadcrumb as part of it’s base CSS, so let’s add a partial that outputs the UL in bootstrap happy CSS. We’ll create it in the view directory of the Application module (this partial is application wide, rather than album specific): module/Application/view/partial/breadcrumb.phtml 19.4. Adding the Menu View Helper 93
  • 132. Zend Framework 2 Documentation, Release 2.2.5 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <ul class="breadcrumb"> <?php // iterate through the pages foreach ($this->pages as $key => $page): ?> <li> <?php // if this isn’t the last page, add a link and the separator if ($key < count($this->pages) - 1): ?> <a href="<?php echo $page->getHref(); ?>"><?php echo $page->getLabel(); ?></a> <span class="divider">/</span> <?php // otherwise, just output the name else: ?> <?php echo $page->getLabel(); ?> <?php endif; ?> </li> <?php endforeach; ?> </ul> Notice how the partial is passed a ZendViewModelViewModel instance with the pages property set to an array of pages to render. Now all we have to do is tell the breadcrumb helper to use the partial we have just written: module/Application/view/layout/layout.phtml 1 2 3 4 5 6 7 8 9 10 11 ... <div class="container"> <?php echo $this->navigation(’navigation’) // <-- Update this!! ->breadcrumbs() ->setMinDepth(0) ->setPartial(array(’partial/breadcrumb.phtml’, ’Album’)); ?> <?php echo $this->content; ?> </div> ... Refreshing the page now gives us a lovely styled set of breadcrumbs on each page. 94 Chapter 19. Using ZendNavigation in your Album Module
  • 133. CHAPTER 20 Using ZendPaginator in your Album Module In this tutorial we will use the ZendPaginator component to add a handy pagination controller to the bottom of the album list. Currently, we only have a handful of albums to display, so showing everything on one page is not a problem. However, how will the album list look when we have 100 albums or more in our database? The standard solution to this problem is to split the data up into a number of pages, and allow the user to navigate around these pages using a pagination control. Just type “Zend Framework” into Google, and you can see their pagination control at the bottom of the page: 20.1 Preparation In order for us to have lots of albums in our database, you’ll need to run the following SQL insert statement to insert the current 150 top iTunes albums (at the time of writing!): INSERT INTO ‘album‘ (‘artist‘, ‘title‘) VALUES (’David Bowie’, ’The Next Day (Deluxe Version)’), (’Bastille’, ’Bad Blood’), (’Bruno Mars’, ’Unorthodox Jukebox’), (’Emeli Sand’, ’Our Version of Events (Special Edition)’), (’Bon Jovi’, ’What About Now (Deluxe Version)’), (’Justin Timberlake’, ’The 20/20 Experience (Deluxe Version)’), (’Bastille’, ’Bad Blood (The Extended Cut)’), (’P!nk’, ’The Truth About Love’), (’Sound City - Real to Reel’, ’Sound City - Real to Reel’), (’Jake Bugg’, ’Jake Bugg’), (’Various Artists’, ’The Trevor Nelson Collection’), 95
  • 134. Zend Framework 2 Documentation, Release 2.2.5 (’David Bowie’, ’The Next Day’), (’Mumford & Sons’, ’Babel’), (’The Lumineers’, ’The Lumineers’), (’Various Artists’, ’Get Ur Freak On - R&B Anthems’), (’The 1975’, ’Music For Cars EP’), (’Various Artists’, ’Saturday Night Club Classics - Ministry of Sound’), (’Hurts’, ’Exile (Deluxe)’), (’Various Artists’, ’Mixmag - The Greatest Dance Tracks of All Time’), (’Ben Howard’, ’Every Kingdom’), (’Stereophonics’, ’Graffiti On the Train’), (’The Script’, ’#3’), (’Stornoway’, ’Tales from Terra Firma’), (’David Bowie’, ’Hunky Dory (Remastered)’), (’Worship Central’, ’Let It Be Known (Live)’), (’Ellie Goulding’, ’Halcyon’), (’Various Artists’, ’Dermot O’Leary Presents the Saturday Sessions 2013’), (’Stereophonics’, ’Graffiti On the Train (Deluxe Version)’), (’Dido’, ’Girl Who Got Away (Deluxe)’), (’Hurts’, ’Exile’), (’Bruno Mars’, ’Doo-Wops & Hooligans’), (’Calvin Harris’, ’18 Months’), (’Olly Murs’, ’Right Place Right Time’), (’Alt-J (?)’, ’An Awesome Wave’), (’One Direction’, ’Take Me Home’), (’Various Artists’, ’Pop Stars’), (’Various Artists’, ’Now That’s What I Call Music! 83’), (’John Grant’, ’Pale Green Ghosts’), (’Paloma Faith’, ’Fall to Grace’), (’Laura Mvula’, ’Sing To the Moon (Deluxe)’), (’Duke Dumont’, ’Need U (100%) [feat. A*M*E] - EP’), (’Watsky’, ’Cardboard Castles’), (’Blondie’, ’Blondie: Greatest Hits’), (’Foals’, ’Holy Fire’), (’Maroon 5’, ’Overexposed’), (’Bastille’, ’Pompeii (Remixes) - EP’), (’Imagine Dragons’, ’Hear Me - EP’), (’Various Artists’, ’100 Hits: 80s Classics’), (’Various Artists’, ’Les Misrables (Highlights From the Motion Picture Soundtrack)’), (’Mumford & Sons’, ’Sigh No More’), (’Frank Ocean’, ’Channel ORANGE’), (’Bon Jovi’, ’What About Now’), (’Various Artists’, ’BRIT Awards 2013’), (’Taylor Swift’, ’Red’), (’Fleetwood Mac’, ’Fleetwood Mac: Greatest Hits’), (’David Guetta’, ’Nothing But the Beat Ultimate’), (’Various Artists’, ’Clubbers Guide 2013 (Mixed By Danny Howard) - Ministry of Sound’), (’David Bowie’, ’Best of Bowie’), (’Laura Mvula’, ’Sing To the Moon’), (’ADELE’, ’21’), (’Of Monsters and Men’, ’My Head Is an Animal’), (’Rihanna’, ’Unapologetic’), (’Various Artists’, ’BBC Radio 1’s Live Lounge - 2012’), (’Avicii & Nicky Romero’, ’I Could Be the One (Avicii vs. Nicky Romero)’), (’The Streets’, ’A Grand Don’t Come for Free’), (’Tim McGraw’, ’Two Lanes of Freedom’), (’Foo Fighters’, ’Foo Fighters: Greatest Hits’), (’Various Artists’, ’Now That’s What I Call Running!’), (’Swedish House Mafia’, ’Until Now’), 96 Chapter 20. Using ZendPaginator in your Album Module
  • 135. Zend Framework 2 Documentation, Release 2.2.5 (’The xx’, ’Coexist’), (’Five’, ’Five: Greatest Hits’), (’Jimi Hendrix’, ’People, Hell & Angels’), (’Biffy Clyro’, ’Opposites (Deluxe)’), (’The Smiths’, ’The Sound of the Smiths’), (’The Saturdays’, ’What About Us - EP’), (’Fleetwood Mac’, ’Rumours’), (’Various Artists’, ’The Big Reunion’), (’Various Artists’, ’Anthems 90s - Ministry of Sound’), (’The Vaccines’, ’Come of Age’), (’Nicole Scherzinger’, ’Boomerang (Remixes) - EP’), (’Bob Marley’, ’Legend (Bonus Track Version)’), (’Josh Groban’, ’All That Echoes’), (’Blue’, ’Best of Blue’), (’Ed Sheeran’, ’+’), (’Olly Murs’, ’In Case You Didn’t Know (Deluxe Edition)’), (’Macklemore & Ryan Lewis’, ’The Heist (Deluxe Edition)’), (’Various Artists’, ’Defected Presents Most Rated Miami 2013’), (’Gorgon City’, ’Real EP’), (’Mumford & Sons’, ’Babel (Deluxe Version)’), (’Various Artists’, ’The Music of Nashville: Season 1, Vol. 1 (Original Soundtrack)’), (’Various Artists’, ’The Twilight Saga: Breaking Dawn, Pt. 2 (Original Motion Picture Soundtrack) (’Various Artists’, ’Mum - The Ultimate Mothers Day Collection’), (’One Direction’, ’Up All Night’), (’Bon Jovi’, ’Bon Jovi Greatest Hits’), (’Agnetha Fltskog’, ’A’), (’Fun.’, ’Some Nights’), (’Justin Bieber’, ’Believe Acoustic’), (’Atoms for Peace’, ’Amok’), (’Justin Timberlake’, ’Justified’), (’Passenger’, ’All the Little Lights’), (’Kodaline’, ’The High Hopes EP’), (’Lana Del Rey’, ’Born to Die’), (’JAY Z & Kanye West’, ’Watch the Throne (Deluxe Version)’), (’Biffy Clyro’, ’Opposites’), (’Various Artists’, ’Return of the 90s’), (’Gabrielle Aplin’, ’Please Don’t Say You Love Me - EP’), (’Various Artists’, ’100 Hits - Driving Rock’), (’Jimi Hendrix’, ’Experience Hendrix - The Best of Jimi Hendrix’), (’Various Artists’, ’The Workout Mix 2013’), (’The 1975’, ’Sex’), (’Chase & Status’, ’No More Idols’), (’Rihanna’, ’Unapologetic (Deluxe Version)’), (’The Killers’, ’Battle Born’), (’Olly Murs’, ’Right Place Right Time (Deluxe Edition)’), (’A$AP Rocky’, ’LONG.LIVE.A$AP (Deluxe Version)’), (’Various Artists’, ’Cooking Songs’), (’Haim’, ’Forever - EP’), (’Lianne La Havas’, ’Is Your Love Big Enough?’), (’Michael Bubl’, ’To Be Loved’), (’Daughter’, ’If You Leave’), (’The xx’, ’xx’), (’Eminem’, ’Curtain Call’), (’Kendrick Lamar’, ’good kid, m.A.A.d city (Deluxe)’), (’Disclosure’, ’The Face - EP’), (’Palma Violets’, ’180’), (’Cody Simpson’, ’Paradise’), (’Ed Sheeran’, ’+ (Deluxe Version)’), 20.1. Preparation 97
  • 136. Zend Framework 2 Documentation, Release 2.2.5 (’Michael Bubl’, ’Crazy Love (Hollywood Edition)’), (’Bon Jovi’, ’Bon Jovi Greatest Hits - The Ultimate Collection’), (’Rita Ora’, ’Ora’), (’g33k’, ’Spabby’), (’Various Artists’, ’Annie Mac Presents 2012’), (’David Bowie’, ’The Platinum Collection’), (’Bridgit Mendler’, ’Ready or Not (Remixes) - EP’), (’Dido’, ’Girl Who Got Away’), (’Various Artists’, ’Now That’s What I Call Disney’), (’The 1975’, ’Facedown - EP’), (’Kodaline’, ’The Kodaline - EP’), (’Various Artists’, ’100 Hits: Super 70s’), (’Fred V & Grafix’, ’Goggles - EP’), (’Biffy Clyro’, ’Only Revolutions (Deluxe Version)’), (’Train’, ’California 37’), (’Ben Howard’, ’Every Kingdom (Deluxe Edition)’), (’Various Artists’, ’Motown Anthems’), (’Courteeners’, ’ANNA’), (’Johnny Marr’, ’The Messenger’), (’Rodriguez’, ’Searching for Sugar Man’), (’Jessie Ware’, ’Devotion’), (’Bruno Mars’, ’Unorthodox Jukebox’), (’Various Artists’, ’Call the Midwife (Music From the TV Series)’ ); This gives us a handy extra 150 rows to play with. If you now visit your album list at /album, you’ll see a huge long list of 150+ albums, its ugly. 20.2 Modifying the AlbumTable In order to let ZF2 handle our database queries automatically for us, we will be using the ZendPaginatorAdapterDbSelect paginator adapter. This will automatically manipulate and run a ZendDbSqlSelect object to include the correct LIMIT and WHERE clauses, so that it returns only the right amount of data needed to display the given page. Let’s modify the fetchAll method of the AlbumTable model, so that it can optionally return a paginator object: module/Album/src/Album/Model/AlbumTable.php 1 2 <?php namespace AlbumModel; 3 4 5 6 7 8 use use use use use ZendDbResultSetResultSet; ZendDbTableGatewayTableGateway; ZendDbSqlSelect; ZendPaginatorAdapterDbSelect; ZendPaginatorPaginator; 9 10 11 12 13 14 15 16 17 18 class AlbumTable { ... public function fetchAll($paginated=false) { if ($paginated) { // create a new Select object for the table album $select = new Select(’album’); // create a new result set based on the Album entity 98 Chapter 20. Using ZendPaginator in your Album Module
  • 137. Zend Framework 2 Documentation, Release 2.2.5 $resultSetPrototype = new ResultSet(); $resultSetPrototype->setArrayObjectPrototype(new Album()); // create a new pagination adapter object $paginatorAdapter = new DbSelect( // our configured select object $select, // the adapter to run it against $this->tableGateway->getAdapter(), // the result set to hydrate $resultSetPrototype ); $paginator = new Paginator($paginatorAdapter); return $paginator; 19 20 21 22 23 24 25 26 27 28 29 30 31 } $resultSet = $this->tableGateway->select(); return $resultSet; 32 33 34 } ... 35 36 This will return a fully configured Paginator object. We’ve already told the DbSelect adapter to use our created Select object, to use the adapter that the TableGateway object uses, and also how to hydrate the result into a Album entity in the same fashion as the TableGateway does. This means that our executed and returned paginator results will return Album objects in exactly the same fashion as the non-paginated results. 20.3 Modifying the AlbumController Next, we need to tell the album controller to return a Pagination object instead of a ResultSet. Both these objects can by iterated over to return hydrated Album objects, so we won’t need to make many changes to the view script: module/Album/src/Album/Controller/AlbumController.php 1 2 3 4 5 6 7 8 9 ... public function indexAction() { // grab the paginator from the AlbumTable $paginator = $this->getAlbumTable()->fetchAll(true); // set the current page to what has been passed in query string, or to 1 if none set $paginator->setCurrentPageNumber((int) $this->params()->fromQuery(’page’, 1)); // set the number of items per page to 10 $paginator->setItemCountPerPage(10); 10 return new ViewModel(array( ’paginator’ => $paginator )); 11 12 13 14 15 } ... Here we are getting the configured Paginator object from the AlbumTable, and then telling it to use the page that is optionally passed in the querystring page parameter. We are also telling the paginator we want to display 10 objects per page. 20.4 Updating the View Script Now, let’s just tell the view script to iterate over the pagination view variable, rather than the albums variable: 20.3. Modifying the AlbumController 99
  • 138. Zend Framework 2 Documentation, Release 2.2.5 module/Album/view/album/album/index.phtml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <table class="table"> <tr> <th>Title</th> <th>Artist</th> <th>&nbsp;</th> </tr> <?php foreach ($this->paginator as $album) : // <-- change here! ?> <tr> <td><?php echo $this->escapeHtml($album->title);?></td> <td><?php echo $this->escapeHtml($album->artist);?></td> <td> <a href="<?php echo $this->url(’album’, array(’action’ => ’edit’, ’id’ => $album->id));?>">Edit</a> <a href="<?php echo $this->url(’album’, array(’action’ => ’delete’, ’id’ => $album->id));?>">Delete</a> </td> </tr> <?php endforeach; ?> </table> Checking the /album route on your website should now give you a list of just 10 albums, but with no method to navigate through the pages. Let’s correct that now... 20.5 Creating the Pagination Control Partial Much like we created a custom breadcrumbs partial to render our breadcrumb in the last tutorial, we need to create a custom pagination control partial to render our pagination control just the way we want it. Again, because we are using Twitter Bootstrap, this should be as simple as outputting correctly formatted html to get a pretty control. Let’s create the partial in the module/Application/view/partial/ folder, so that we can use the control in all our modules: module/Application/view/partial/paginator.phtml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php if ($this->pageCount): ?> <div class="pagination pagination-centered"> <ul> <!-- Previous page link --> <?php if (isset($this->previous)): ?> <li> <a href="<?php echo $this->url($this->route); ?>?page=<?php echo $this->previous << </a> </li> <?php else: ?> <li class="disabled"> <a href="#"> << </a> </li> <?php endif; ?> 18 <!-- Numbered page links --> <?php foreach ($this->pagesInRange as $page): ?> <?php if ($page != $this->current): ?> <li> 19 20 21 22 100 Chapter 20. Using ZendPaginator in your Album Module
  • 139. Zend Framework 2 Documentation, Release 2.2.5 23 24 25 26 27 28 29 30 31 32 <a href="<?php echo $this->url($this->route);?>?page=<?php echo $page; ?>"> <?php echo $page; ?> </a> </li> <?php else: ?> <li class="active"> <a href="#"><?php echo $page; ?></a> </li> <?php endif; ?> <?php endforeach; ?> 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 <!-- Next page link --> <?php if (isset($this->next)): ?> <li> <a href="<?php echo $this->url($this->route); ?>?page=<?php echo $this->next; ?> >> </a> </li> <?php else: ?> <li class="disabled"> <a href="#"> >> </a> </li> <?php endif; ?> </ul> </div> <?php endif; ?> All this partial does is to create a pagination control with links to the correct pages (if there is more than one page in the pagination object). It will render a previous page link (and mark it disabled if you are at the first page), then render a list of intermediate pages (that are passed to the partial based on the rendering style – we’ll set in the view helper in the next step). Finally, it will create a next page link (and disable it if you’re at the end). Notice how we pass the page number via the page querystring parameter which we have already told our controller to use to display the current page. 20.5. Creating the Pagination Control Partial 101
  • 140. Zend Framework 2 Documentation, Release 2.2.5 102 Chapter 20. Using ZendPaginator in your Album Module
  • 141. CHAPTER 21 Using the PaginationControl View Helper The only thing left for us to do so that we can page through the albums is to use the paginationControl view helper to display our pagination control. This is nicely straightforward as we have already done all the ground work needed to display the control: module/Album/view/album/album/index.phtml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ... <?php // add at the end of the file after the table echo $this->paginationControl( // the paginator object $this->paginator, // the scrolling style ’sliding’, // the partial to use to render the control array(’partial/paginator.phtml’, ’Album’), // the route to link to when a user clicks a control link array( ’route’ => ’album’ ) ); ?> All we need to do here is to echo the paginationControl helper, and tell it to use our paginator object, sliding scrolling style, our paginator partial, and which route to use for clicks. Refreshing your application should give you a lovely bootstrap styled pagination control! 103
  • 142. Zend Framework 2 Documentation, Release 2.2.5 104 Chapter 21. Using the PaginationControl View Helper
  • 143. CHAPTER 22 Setting up a database adapter 22.1 Introduction In most cases, e.g. in your controllers, your database adapter can be fetched directly from the service manager. Some classes however, like ZendValidatorDbRecordExists isn’t aware of the service manager, but still needs an adapter to function. There are many different ways to provide this functionality to your application. Below are a few examples. 22.2 Basic setup Normally you will setup your database adapter using a factory in the service manager in your configuration. It might look something like this: 1 // config/autoload/global.php 2 3 4 5 6 7 8 9 10 11 12 13 return array( ’db’ => array( ’driver’ => ’Pdo’, ’dsn’ => ’mysql:dbname=zf2tutorial;host=localhost’, ), ’service_manager’ => array( ’factories’ => array( ’ZendDbAdapterAdapter’ => ’ZendDbAdapterAdapterServiceFactory’, ), ), ); The adapter can then be accessed in any ServiceLocatorAware classes. 1 2 3 4 5 6 7 8 public function getAdapter() { if (!$this->adapter) { $sm = $this->getServiceLocator(); $this->adapter = $sm->get(’ZendDbAdapterAdapter’); } return $this->adapter; } 105
  • 144. Zend Framework 2 Documentation, Release 2.2.5 More information on adapter options can be found in the docs for ZendDbAdapter. 22.3 Setting a static adapter In order to utilize this adapter in non-ServiceLocatorAware classes, you ZendDbTableGatewayFeatureGlobalAdapterFeature::setStaticAdapter() static adapter: 1 can use to set a // config/autoload/global.php 2 3 4 5 6 7 8 9 10 11 12 return array( ’db’ => array( ’driver’ => ’Pdo’, ’dsn’ => ’mysql:dbname=zf2tutorial;host=localhost’, ), ’service_manager’ => array( ’factories’ => array( ’ZendDbAdapterAdapter’ => function ($serviceManager) { $adapterFactory = new ZendDbAdapterAdapterServiceFactory(); $adapter = $adapterFactory->createService($serviceManager); 13 ZendDbTableGatewayFeatureGlobalAdapterFeature::setStaticAdapter($adapter); 14 15 return $adapter; 16 } 17 ), 18 ), 19 20 ); The adapter can then later be fetched using ZendDbTableGatewayFeatureGlobalAdapterFeature::getStaticAda for use in e.g. ZendValidatorDbRecordExists: 1 2 3 4 5 6 7 $validator = new ZendValidatorDbRecordExists( array( ’table’ => ’users’, ’field’ => ’emailaddress’, ’adapter’ => ZendDbTableGatewayFeatureGlobalAdapterFeature::getStaticAdapter() ) ); 106 Chapter 22. Setting up a database adapter
  • 145. CHAPTER 23 Migration from Zend Framework 1 This guide is intended to provide tools and strategies for migrating from Zend Framework 1 to Zend Framework 2. There is no single solution that will work for every project, nor any tools to automate the process. In this guide, we will cover the following: • Tools for namespacing your code. • Tools for consuming Zend Framework 2 within your Zend Framework 1 application. • Strategies for running Zend Framework 2 and Zend Framework 1 in parallel. • Strategies for making your code easier to migrate, focussing primarily on clean separation of your domain logic and the MVC layer. • Strategies for migrating the MVC layer. • Strategies for migrating your domain layer. 107
  • 146. Zend Framework 2 Documentation, Release 2.2.5 108 Chapter 23. Migration from Zend Framework 1
  • 147. CHAPTER 24 Namespacing Old Classes ZF2’s minimal version is PHP 5.3. The most notable feature of PHP 5.3 is the addition of namespaces, which ZF2 fully embraces. Moreover, new projects built on ZF2 also fully embrace PHP namespaces. The addition of namespaces to PHP has greatly improved the readability of long class names and has helped better organize code into modules and components. This transition has also given birth to some naming best practices that help developers organize their code bases consisting of classes, components, and modules in a consistent and clean fashion. Converting an older code base that follows the original PEAR/ZF underscore separated class naming convention into a properly namespaced codebase is one of the easier strategies to employ in both modernizing your code base as well as getting ready to ZF2-ify your ZF1 application. We’ve created a tool to help in this endeavor, it is located here: https://github.com/zendframework/Namespacer This tool will take a wholesale approach to converting older code like the following: class My_Long_NestedComponent_ClassName { // methods that use other classes } into: namespace MyLongNestedComponent; use OtherClasses; use SomethingElseConsumed; class ClassName { // methods with classes converted to short name from use statement. } Some IDEs have this capability to some degree. That said, a good approach might be to use the command line Namespacer to do a full sweep of your codebase, then use the IDE to make more specific naming changes that might makes more sense to your application. 109
  • 148. Zend Framework 2 Documentation, Release 2.2.5 24.1 Namespacing a ZF1 Application The above Namespacer is a generalized tool. It does not understand the structure and naming conventions of a ZF1 application. As such, you’ll need to address the problem of converting your classes according to their role, and which classes you find you can convert without affecting the way the framework interoperates with your code. For example, in ZF1, the naming convention of application and module layer classes does not directly match up with same well-defined library class/file conventions of the PEAR/ZF namings. For a standard ZF1 application, in the application/ directory, controller classes are not prefixed, yet model and form classes are prefixed with Application_. Moreover, they exist inside of lowercased directories, such as models or forms, and their file to class name segment matching picks up only after the first segment. As an example, you might have this directory structure with the class names on the right: application/ -- Bootstrap.php -- configs | -- application.ini | -- application.ini.dist -- controllers | -- IndexController.php [class IndexController] | -- PurchaseOrderController.php [class PurchaseOrderController] -- forms | -- PurchaseOrder | -- Payment.php [class Application_Form_PurchaseOrder_Payment] -- layouts | -- scripts | -- main.phtml | -- subpage.phtml -- models | -- DbTable | | -- Invoice.php [Application_Model_DbTable_Invoice] | -- Invoice.php [Application_Model_Invoice] | -- InvoiceRepository.php [Application_Model_InvoiceRepository] | -- Payment | | -- Paypal | | -- DirectPayment.php [Application_Model_Payment_Paypal_DirectPayment] | -- PurchaseOrder.php [Application_Model_PurchaseOrder] -- views -- scripts -- error | -- error.phtml -- index | -- index.phtml -- purchase-order -- index.phtml -- purchaser.phtml It would not be a good strategy to attempt to do a wholesale namespacing of this kind of project for a number of reasons: 1. ZF1 has special, context-aware autoloaders that will assist loading a class of a particular context from a special location on disk. For example, ZF1 understands controllers will be located in the controllers directory and will not be prefixed unless they are inside of a named module’s controllers directory. 2. Attempting to apply namespacing to controller classes would generally render a ZF1 application useless. ZF1, beyond loading files from disk, assumes controllers will have a very specific naming convention so that they can be invoked by the framework upon routing and dispatching. 3. Beyond dispatching, ZF1 uses the class name to identify and map the proper view script to automatically execute. 110 Chapter 24. Namespacing Old Classes
  • 149. Zend Framework 2 Documentation, Release 2.2.5 By naming the controller something non-standard, views will no longer this this 1:1 mapping of controllers by name to controller action named view scripts. A better solution would be to start by namespacing the parts of your ZF1 application that have fewer tie-ins with the ZF1 architecture. The place to start with this is models and forms. Since models and forms do not touch controller and view classes (which make heavy use of ZF1 classes by way of inheritance), model and form classes might not have the same level of coupling. 24.2 HOWTO Namespace Your Models First, ensure your classes are under version control. The namespacer tool will make modification to classes in place. You can then use your version control system as a diffing utility afterwards . To run the tool, download the phar. Optionally you can place the namespacer.phar into a directory in your PATH. Namespacing is a 2 part process: 1. Create a map of all the old files, new files, old classes and new classes. 2. Make the transformations according to the map file. Change into your models/ directory and execute the map function: namespacer.phar map --mapfile model-map.php --source models/ This will produce a file called model-map.php with entries like this: 1 2 3 4 5 6 7 8 9 10 11 <?php return array ( array ( ’root_directory’ => ’/realpath/to/project/application/models’, ’original_class’ => ’Application_Model_Invoice’, ’original_file’ => ’/realpath/to/project/application/models/Invoice.php’, ’new_namespace’ => ’ApplicationModel’, ’new_class’ => ’Invoice’, ’new_file’ => ’/realpath/to/project/application/models/Application/Model/Invoice.php’, ), ... ); This gives you an opportunity to manually edit the transformations if you so desire. While you can modify this file, you also might find it to be easier to go with the default transformations, and do the remaining changes with your IDE’s refactoring utility. Once you are happy with the map file, run the transformations: namespacer.phar transform --mapfile model-map.php At this point, you can use your version control system’s status command to see how the directory has transformed. As an example, in a sample project of mine, git reports the following: renamed: new file: renamed: renamed: renamed: renamed: new file: renamed: renamed: models/DbTable/Invoice.php -> models/Application/Model/DbTable/Invoice.php models/Application/Model/DbTable/Transaction.php models/Invoice.php -> models/Application/Model/Invoice.php models/Payment/Paypal/DirectPayment.php -> models/Application/Model/Payment/Paypal/DirectPa models/PurchaseOrder.php -> models/Application/Model/PurchaseOrder.php models/PurchaseOrderRepository.php -> models/Application/Model/PurchaseOrderRepository.php models/Application/Model/PurchaseOrderService.php models/Purchaser.php -> models/Application/Model/Purchaser.php models/Ticket.php -> models/Application/Model/Ticket.php 24.2. HOWTO Namespace Your Models 111
  • 150. Zend Framework 2 Documentation, Release 2.2.5 renamed: renamed: deleted: deleted: models/Transaction.php -> models/Application/Model/Transaction.php models/TransactionRepository.php -> models/Application/Model/TransactionRepository.php models/DbTable/Transaction.php models/PurchaseOrderService.php You’ll notice that the resulting files have treated the models/ directory as the autoloader root directory. That means that from this root, class files follow the strict PEAR/ZF2 classfile naming convention. The contents of one of the files will look like this: 1 namespace ApplicationModel; 2 3 4 5 use ApplicationModelPurchaseOrder; use ApplicationModelTransaction; use Zend_Filter_Alnum; 6 7 8 class Invoice { 9 protected $tickets; protected $transaction; 10 11 12 ... 13 14 } Things to notice here: • A namespace has been created for this class. • The namespacer has created PHP use statements for classes known in the map file. • Unknown classes are also included (for example, Zend classes) in use statements. By keeping the old ZF1 classes, your models should continue to work if they consume ZF1 classes. This will allow you to, at your own pace, transition your codebase to ZF2. This same procedure can largely be adapted to forms and independent library code as well. 112 Chapter 24. Namespacing Old Classes
  • 151. CHAPTER 25 Running Zend Framework 2 and Zend Framework 1 in parallel From a technical point of view it is absolutely possible to run ZF2 in parallel with ZF1 because there is no conflict between the classnames due to the fact that ZF2 uses namespaces and ZF1 does not. Running ZF1 and ZF2 in parallel can be used as a migration strategy in projects where it is not possible, or not convenient, to migrate an entire application from ZF1 to ZF2. For instance, you could implement any new features of the application using ZF2, while maintaining original ZF1 features. Let’s examine some scenarios on how to execute ZF1 and ZF2 together. 25.1 Use ZF2 in a ZF1 project Suppose we have an existing ZF1 application and we want to start using ZF2; how could we do that? Because ZF2 uses namespaced classes, you can run it in parallel with ZF1 without naming conflicts. In order to do this, you will need to add some code to autoload ZF2 from within your ZF1 project. Add these lines of code in your public/index.php, before the instantiation of $application: 1 2 3 4 5 6 define(’ZF2_PATH’, ’/path/to/zf2/library’); require_once ZF2_PATH . ’/Zend/Loader/StandardAutoloader.php’; $loader = new ZendLoaderStandardAutoloader(array( ’autoregister_zf’ => true, )); $loader->register(); We used the StandardAutoloader class from ZF2. Using this autoloader, classes with the initial namespace Zend will be loaded using the ZF2_PATH, and any ZF1 classes will continue to be loaded via the mechanisms present in ZF1. Of course, this is not a real integration of ZF2 inside ZF1; it only provides the ability to consume ZF2 classes within your ZF1 application. For instance, you cannot use the MVC architecture of ZF2 because you are using the MVC of ZF1. Evan Coury, a member of the ZF community review team, has produced a nice module for ZF1 (zf-2-for-1) that allows you to use ZF2 features inside an existing ZF1 application. This module offers some basic integrations like the usage of ZF2 view helpers in the ZF1 view layer (i.e. $this->zf2->get(’formRow’)). 113
  • 152. Zend Framework 2 Documentation, Release 2.2.5 25.2 Use ZF1 in a ZF2 project You can add ZF1 to your ZF2 application via Composer by adding the “zendframework/zendframework1” package as a requirement. For instance, if you have a ZF2 application and you want to install ZF 1.12, you need to add the following line in the require section of your composer.json file: "require": { "php": ">=5.3.3", "zendframework/zendframework1": "1.12", ... } After executing composer.phar update, you can start to use ZF1 classes in your ZF2 project. Since all ZF1 classes exist in the global namespace, you will need to refer to them by their full name; as examples, Zend_Date, Zend_Feed_Reader, etc. For other strategies on how to use ZF1 in a ZF2 project, you can check out this blog post by Abdul Malik Ikhsan, Zend Framework 2 : Using Zend Framework 1 libraries. 25.3 Run ZF1 and ZF2 together As we mentioned early, one way to migrate a ZF1 application to ZF2 can be to execute in parallel the different versions of the framework, using ZF2 for the new features, and migrating the ZF1 code step by step. In order to execute in parallel, we need to map different URLs to the different front controllers for ZF1 and ZF2. This goal can be accomplished using the rewriting rules of your web server. From a performance point of view, this is the best solution because it does not involve pre-processing overhead. For each URL we can define a different version of the framework to be used. For instance, imagine we have a ZF1 application and we want to use ZF2 only for URLs starting with /album. We can use the following .htaccess file (this information is related to apache; if you are using another web server, read the instructions in the note below): 1 2 3 4 5 6 7 8 SetEnv APPLICATION_ENV development RewriteEngine On RewriteCond %{REQUEST_FILENAME} -s [OR] RewriteCond %{REQUEST_FILENAME} -l [OR] RewriteCond %{REQUEST_FILENAME} -d RewriteRule ^.*$ - [NC,L] RewriteRule ^album(/.*)?$ index_zf2.php [NC,L] RewriteRule ^.*$ index.php [NC,L] index_zf2.php is a PHP script that includes as the typical public/index.php file of ZF2. Here is the source code for index_zf2.php: 1 require_once ’../path-to-ZF2-app/public/index.php’; We suggest putting the ZF2 application in a separate folder under the same root directory of the ZF1 application. In this way you can continue to maintain the existing ZF1 code and use ZF2 only for the new features. Moreover, if you want to migrate the old code you can do that by URL and switch to the new ZF2 code only when you are ready. This approach can be useful to provide migration guideline without losing development time in a full stack migration. Note: All web servers support a rewriting mechanism. For instance, if you are using Microsoft IIS 7, you can check how to configure the rewriting rules from Rob Allen’s post Zend Framework URL Rewriting in IIS7; if you are using nginx, you can check out this StackOverflow question: Zend Framework on nginx. 114 Chapter 25. Running Zend Framework 2 and Zend Framework 1 in parallel
  • 153. Zend Framework 2 Documentation, Release 2.2.5 25.3. Run ZF1 and ZF2 together 115
  • 154. Zend Framework 2 Documentation, Release 2.2.5 116 Chapter 25. Running Zend Framework 2 and Zend Framework 1 in parallel
  • 155. CHAPTER 26 Introduction to ZendAuthentication The ZendAuthentication component provides an API for authentication and includes concrete authentication adapters for common use case scenarios. ZendAuthentication is concerned only with authentication and not with authorization. Authentication is loosely defined as determining whether an entity actually is what it purports to be (i.e., identification), based on some set of credentials. Authorization, the process of deciding whether to allow an entity access to, or to perform operations upon, other entities is outside the scope of ZendAuthentication. For more information about authorization and access control with Zend Framework, please see the ZendPermissionsAcl or ZendPermissionsRbac component. Note: There is no ZendAuthenticationAuthentication class, instead the class ZendAuthenticationAuthenticationService is provided. This class uses underlying authentication adapters and persistent storage backends. 26.1 Adapters ZendAuthentication adapters are used to authenticate against a particular type of authentication service, such as LDAP, RDBMS, or file-based storage. Different adapters are likely to have vastly different options and behaviors, but some basic things are common among authentication adapters. For example, accepting authentication credentials (including a purported identity), performing queries against the authentication service, and returning results are common to ZendAuthentication adapters. Each ZendAuthentication adapter class implements ZendAuthenticationAdapterAdapterInterface. This interface defines one method, authenticate(), that an adapter class must implement for performing an authentication query. Each adapter class must be prepared prior to calling authenticate(). Such adapter preparation includes setting up credentials (e.g., username and password) and defining values for adapter-specific configuration options, such as database connection settings for a database table adapter. The following is an example authentication adapter that requires a username and password to be set for authentication. Other details, such as how the authentication service is queried, have been omitted for brevity: 1 use ZendAuthenticationAdapterAdapterInterface; 2 3 4 5 6 class MyAuthAdapter implements AdapterInterface { /** * Sets username and password for authentication 117
  • 156. Zend Framework 2 Documentation, Release 2.2.5 * * @return void */ public function __construct($username, $password) { // ... } 7 8 9 10 11 12 13 14 /** * Performs an authentication attempt * * @return ZendAuthenticationResult * @throws ZendAuthenticationAdapterExceptionExceptionInterface If authentication cannot be performed * */ public function authenticate() { // ... } 15 16 17 18 19 20 21 22 23 24 25 26 } As indicated in its docblock, authenticate() must return an instance of ZendAuthenticationResult (or of a class derived from ZendAuthenticationResult). If for some reason performing an authentication query is impossible, authenticate() should throw an exception that derives from ZendAuthenticationAdapterExceptionExceptionInterface. 26.2 Results ZendAuthentication adapters return an instance of ZendAuthenticationResult with authenticate() in order to represent the results of an authentication attempt. Adapters populate the ZendAuthenticationResult object upon construction, so that the following four methods provide a basic set of user-facing operations that are common to the results of ZendAuthentication adapters: • isValid()- returns TRUE if and only if the result represents a successful authentication attempt • getCode()- returns a ZendAuthenticationResult constant identifier for determining the type of authentication failure or whether success has occurred. This may be used in situations where the developer wishes to distinguish among several authentication result types. This allows developers to maintain detailed authentication result statistics, for example. Another use of this feature is to provide specific, customized messages to users for usability reasons, though developers are encouraged to consider the risks of providing such detailed reasons to users, instead of a general authentication failure message. For more information, see the notes below. • getIdentity()- returns the identity of the authentication attempt • getMessages()- returns an array of messages regarding a failed authentication attempt A developer may wish to branch based on the type of authentication result in order to perform more specific operations. Some operations developers might find useful are locking accounts after too many unsuccessful password attempts, flagging an IP address after too many nonexistent identities are attempted, and providing specific, customized authentication result messages to the user. The following result codes are available: 1 use ZendAuthenticationResult; 2 3 4 5 6 Result::SUCCESS Result::FAILURE Result::FAILURE_IDENTITY_NOT_FOUND Result::FAILURE_IDENTITY_AMBIGUOUS 118 Chapter 26. Introduction to ZendAuthentication
  • 157. Zend Framework 2 Documentation, Release 2.2.5 7 8 Result::FAILURE_CREDENTIAL_INVALID Result::FAILURE_UNCATEGORIZED The following example illustrates how a developer may branch on the result code: 1 2 // inside of AuthController / loginAction $result = $this->auth->authenticate($adapter); 3 4 switch ($result->getCode()) { 5 case Result::FAILURE_IDENTITY_NOT_FOUND: /** do stuff for nonexistent identity **/ break; 6 7 8 9 case Result::FAILURE_CREDENTIAL_INVALID: /** do stuff for invalid credential **/ break; 10 11 12 13 case Result::SUCCESS: /** do stuff for successful authentication **/ break; 14 15 16 17 default: /** do stuff for other failure **/ break; 18 19 20 21 } 26.3 Identity Persistence Authenticating a request that includes authentication credentials is useful per se, but it is also important to support maintaining the authenticated identity without having to present the authentication credentials with each request. HTTP is a stateless protocol, however, and techniques such as cookies and sessions have been developed in order to facilitate maintaining state across multiple requests in server-side web applications. 26.3.1 Default Persistence in the PHP Session By default, ZendAuthentication provides persistent storage of the identity from a successful authentication attempt using the PHP session. Upon a successful authentication attempt, ZendAuthenticationAuthenticationService::authenticate() stores the identity from the authentication result into persistent storage. Unless specified otherwise, ZendAuthenticationAuthenticationService uses a storage class named ZendAuthenticationStorageSession, which, in turn, uses ZendSession. A custom class may instead be used by providing an object that implements ZendAuthenticationStorageStorageInterface to ZendAuthenticationAuthenticationService::setStorage(). Note: If automatic persistent storage of the identity is not appropriate for a particular use case, then developers may forget using the ZendAuthenticationAuthenticationService class altogether, instead using an adapter class directly. 26.3. Identity Persistence 119
  • 158. Zend Framework 2 Documentation, Release 2.2.5 Modifying the Session Namespace ZendAuthenticationStorageSession uses a session namespace of ‘Zend_Auth‘. This namespace may be overridden by passing a different value to the constructor of ZendAuthenticationStorageSession, and this value is internally passed along to the constructor of ZendSessionContainer. This should occur before authentication is attempted, since ZendAuthenticationAuthenticationService::authenticate() performs the automatic storage of the identity. 1 2 use ZendAuthenticationAuthenticationService; use ZendAuthenticationStorageSession as SessionStorage; 3 4 $auth = new AuthenticationService(); 5 6 7 // Use ’someNamespace’ instead of ’Zend_Auth’ $auth->setStorage(new SessionStorage(’someNamespace’)); 8 9 10 11 /** * @todo Set up the auth adapter, $authAdapter */ 12 13 14 15 // Authenticate, saving the result, and persisting the identity on // success $result = $auth->authenticate($authAdapter); 26.3.2 Chain Storage A website may have multiple storage in place. The Chain Storage can be used to glue these together. The Chain can for example be configured to first use a Session Storage and then use a OAuth as a secondary Storage. One could configure this in the following way: 1 2 3 $storage = new Chain; $storage->add(new Session); $storage->add(new OAuth); // Note: imaginary storage, not part of ZF2 Now if the Chain Storage is accessed its underlying Storage will get accessed in the order in which they were added to the chain. Thus first the Session Storage is used. Now either: • The Session Storage is non-empty and the Chain will use its contents. • The Sesssion Storage is empty. Next the OAuth Storage is accessed. – If this one is also empty the Chain will act as empty. – If this one is non-empty the Chain will use its contents. However it will also populate all Storage with higher priority. Thus the Session Storage will be populated with the contents of the Oauth Storage. The priority of Storage in the Chain can be made explicit via the Chain::add method. 1 2 $chain->add(new A, 2); $chain->add(new B, 10); // First use B 26.3.3 Implementing Customized Storage Sometimes developers may need to use a different identity storage mechanism than that provided by ZendAuthenticationStorageSession. For such cases developers may simply imple- 120 Chapter 26. Introduction to ZendAuthentication
  • 159. Zend Framework 2 Documentation, Release 2.2.5 ment ZendAuthenticationStorageStorageInterface and supply an instance of the class to ZendAuthenticationAuthenticationService::setStorage(). Using a Custom Storage Class In order to use an identity persistence storage class other than ZendAuthenticationStorageSession, a developer implements ZendAuthenticationStorageStorageInterface: 1 use ZendAuthenticationStorageStorageInterface; 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class MyStorage implements StorageInterface { /** * Returns true if and only if storage is empty * * @throws ZendAuthenticationExceptionExceptionInterface If it is impossible to * determine whether storage is empty * * @return boolean */ public function isEmpty() { /** * @todo implementation */ } 19 20 21 22 23 24 25 26 27 28 /** * Returns the contents of storage * * Behavior is undefined when storage is empty. * * @throws ZendAuthenticationExceptionExceptionInterface If reading contents from storage is impossible * * @return mixed */ 29 30 31 32 33 34 35 public function read() { /** * @todo implementation */ } 36 37 38 39 40 41 42 43 44 /** * Writes $contents to storage * * @param mixed $contents * @throws ZendAuthenticationExceptionExceptionInterface If writing $contents to storage is impossible * * @return void */ 45 46 47 48 49 public function write($contents) { /** * @todo implementation 26.3. Identity Persistence 121
  • 160. Zend Framework 2 Documentation, Release 2.2.5 */ 50 } 51 52 /** * Clears contents from storage * * @throws ZendAuthenticationExceptionExceptionInterface If clearing contents from storage is impossible * * @return void */ 53 54 55 56 57 58 59 60 public function clear() { /** * @todo implementation */ } 61 62 63 64 65 66 67 } In order to use this custom storage class, ZendAuthenticationAuthenticationService::setStorage() is invoked before an authentication query is attempted: 1 use ZendAuthenticationAuthenticationService; 2 3 4 // Instruct AuthenticationService to use the custom storage class $auth = new AuthenticationService(); 5 6 $auth->setStorage(new MyStorage()); 7 8 9 10 /** * @todo Set up the auth adapter, $authAdapter */ 11 12 13 14 // Authenticate, saving the result, and persisting the identity on // success $result = $auth->authenticate($authAdapter); 26.4 Usage There are two provided ways to use ZendAuthentication adapters: • indirectly, through ZendAuthenticationAuthenticationService::authenticate() • directly, through the adapter’s authenticate() method The following example illustrates how to use a ZendAuthentication adapter indirectly, through the use of the ZendAuthenticationAuthenticationService class: 1 use ZendAuthenticationAuthenticationService; 2 3 4 // instantiate the authentication service $auth = new AuthenticationService(); 5 6 7 // Set up the authentication adapter $authAdapter = new MyAuthAdapter($username, $password); 8 9 // Attempt authentication, saving the result 122 Chapter 26. Introduction to ZendAuthentication
  • 161. Zend Framework 2 Documentation, Release 2.2.5 10 $result = $auth->authenticate($authAdapter); 11 12 13 14 15 16 17 18 19 20 21 22 if (!$result->isValid()) { // Authentication failed; print the reasons why foreach ($result->getMessages() as $message) { echo "$messagen"; } } else { // Authentication succeeded; the identity ($username) is stored // in the session // $result->getIdentity() === $auth->getIdentity() // $result->getIdentity() === $username } Once authentication has been attempted in a request, as in the above example, it is a simple matter to check whether a successfully authenticated identity exists: 1 use ZendAuthenticationAuthenticationService; 2 3 $auth = new AuthenticationService(); 4 5 6 7 /** * @todo Set up the auth adapter, $authAdapter */ 8 9 10 11 12 if ($auth->hasIdentity()) { // Identity exists; get it $identity = $auth->getIdentity(); } To remove an identity from persistent storage, simply use the clearIdentity() method. This typically would be used for implementing an application “logout” operation: 1 $auth->clearIdentity(); When the automatic use of persistent storage is inappropriate for a particular use case, a developer may simply bypass the use of the ZendAuthenticationAuthenticationService class, using an adapter class directly. Direct use of an adapter class involves configuring and preparing an adapter object and then calling its authenticate() method. Adapter-specific details are discussed in the documentation for each adapter. The following example directly utilizes MyAuthAdapter: 1 2 // Set up the authentication adapter $authAdapter = new MyAuthAdapter($username, $password); 3 4 5 // Attempt authentication, saving the result $result = $authAdapter->authenticate(); 6 7 8 9 10 11 12 13 14 15 if (!$result->isValid()) { // Authentication failed; print the reasons why foreach ($result->getMessages() as $message) { echo "$messagen"; } } else { // Authentication succeeded // $result->getIdentity() === $username } 26.4. Usage 123
  • 162. Zend Framework 2 Documentation, Release 2.2.5 124 Chapter 26. Introduction to ZendAuthentication
  • 163. CHAPTER 27 Database Table Authentication 27.1 Introduction ZendAuthenticationAdapterDbTable provides the ability to authenticate against credentials stored in a database table. Because ZendAuthenticationAdapterDbTable requires an instance of ZendDbAdapterAdapter to be passed to its constructor, each instance is bound to a particular database connection. Other configuration options may be set through the constructor and through instance methods, one for each option. The available configuration options include: • tableName: This is the name of the database table that contains the authentication credentials, and against which the database authentication query is performed. • identityColumn: This is the name of the database table column used to represent the identity. The identity column must contain unique values, such as a username or e-mail address. • credentialColumn: This is the name of the database table column used to represent the credential. Under a simple identity and password authentication scheme, the credential value corresponds to the password. See also the credentialTreatment option. • credentialTreatment: In many cases, passwords and other sensitive data are encrypted, hashed, encoded, obscured, salted or otherwise treated through some function or algorithm. By specifying a parameterized treatment string with this method, such as ‘MD5(?)‘ or ‘PASSWORD(?)‘, a developer may apply such arbitrary SQL upon input credential data. Since these functions are specific to the underlying RDBMS, check the database manual for the availability of such functions for your database system. 27.2 Basic Usage As explained in the introduction, the ZendAuthenticationAdapterDbTable constructor requires an instance of ZendDbAdapterAdapter that serves as the database connection to which the authentication adapter instance is bound. First, the database connection should be created. The following code creates an adapter for an in-memory database, creates a simple table schema, and inserts a row against which we can perform an authentication query later. This example requires the PDO SQLite extension to be available: 125
  • 164. Zend Framework 2 Documentation, Release 2.2.5 1 use ZendDbAdapterAdapter as DbAdapter; 2 3 4 5 6 7 // Create a SQLite database connection $dbAdapter = new DbAdapter(array( ’driver’ => ’Pdo_Sqlite’, ’database’ => ’path/to/sqlite.db’ )); 8 9 10 11 12 13 14 // Build a simple table creation query $sqlCreate = ’CREATE TABLE [users] (’ . ’[id] INTEGER NOT NULL PRIMARY KEY, ’ . ’[username] VARCHAR(50) UNIQUE NOT NULL, ’ . ’[password] VARCHAR(32) NULL, ’ . ’[real_name] VARCHAR(150) NULL)’; 15 16 17 // Create the authentication credentials table $dbAdapter->query($sqlCreate); 18 19 20 21 // Build a query to insert a row for which authentication may succeed $sqlInsert = "INSERT INTO users (username, password, real_name) " . "VALUES (’my_username’, ’my_password’, ’My Real Name’)"; 22 23 24 // Insert the data $dbAdapter->query($sqlInsert); With the database connection and table data available, an instance of ZendAuthenticationAdapterDbTable may be created. Configuration option values may be passed to the constructor or deferred as parameters to setter methods after instantiation: 1 use ZendAuthenticationAdapterDbTable as AuthAdapter; 2 3 4 5 6 7 8 // Configure the instance with constructor parameters... $authAdapter = new AuthAdapter($dbAdapter, ’users’, ’username’, ’password’ ); 9 10 11 // ...or configure the instance with setter methods $authAdapter = new AuthAdapter($dbAdapter); 12 13 14 15 16 17 $authAdapter ->setTableName(’users’) ->setIdentityColumn(’username’) ->setCredentialColumn(’password’) ; At this point, the authentication adapter instance is ready to accept authentication queries. In order to formulate an authentication query, the input credential values are passed to the adapter prior to calling the authenticate() method: 1 2 3 4 5 // Set the input credential values (e.g., from a login form) $authAdapter ->setIdentity(’my_username’) ->setCredential(’my_password’) ; 6 7 // Perform the authentication query, saving the result 126 Chapter 27. Database Table Authentication
  • 165. Zend Framework 2 Documentation, Release 2.2.5 In addition to the availability of the getIdentity() method upon the authentication result object, ZendAuthenticationAdapterDbTable also supports retrieving the table row upon authentication success: 1 2 // Print the identity echo $result->getIdentity() . "nn"; 3 4 5 // Print the result row print_r($authAdapter->getResultRowObject()); 6 7 8 /* Output: my_username 9 10 11 12 13 14 15 16 17 Array ( [id] => 1 [username] => my_username [password] => my_password [real_name] => My Real Name ) */ Since the table row contains the credential value, it is important to secure the values against unintended access. When retrieving the result object, we can either specify what columns to return, or what columns to omit: 1 2 3 4 $columnsToReturn = array( ’id’, ’username’, ’real_name’ ); print_r($authAdapter->getResultRowObject($columnsToReturn)); 5 6 /* Output: 7 8 9 10 11 12 13 14 Array ( [id] => 1 [username] => my_username [real_name] => My Real Name ) */ 15 16 17 $columnsToOmit = array(’password’); print_r($authAdapter->getResultRowObject(null, $columnsToOmit); 18 19 /* Output: 20 21 22 23 24 25 26 27 Array ( [id] => 1 [username] => my_username [real_name] => My Real Name ) */ 27.2. Basic Usage 127
  • 166. Zend Framework 2 Documentation, Release 2.2.5 27.3 Advanced Usage: Persisting a DbTable Result Object By default, ZendAuthenticationAdapterDbTable returns the identity supplied back to the auth object upon successful authentication. Another use case scenario, where developers want to store to the persistent storage mechanism of ZendAuthentication an identity object containing other useful information, is solved by using the getResultRowObject() method to return a stdClass object. The following code snippet illustrates its use: 1 2 // authenticate with ZendAuthenticationAdapterDbTable $result = $this->_auth->authenticate($adapter); 3 4 5 6 7 8 9 10 11 if ($result->isValid()) { // store the identity as an object where only the username and // real_name have been returned $storage = $this->_auth->getStorage(); $storage->write($adapter->getResultRowObject(array( ’username’, ’real_name’, ))); 12 // store the identity as an object where the password column has // been omitted $storage->write($adapter->getResultRowObject( null, ’password’ )); 13 14 15 16 17 18 19 /* ... */ 20 21 22 } else { 23 /* ... */ 24 25 26 } 27.3.1 Advanced Usage By Example While the primary purpose of the ZendAuthentication component (and consequently ZendAuthenticationAdapterDbTable) is primarily authentication and not authorization, there are a few instances and problems that toe the line between which domain they fit within. Depending on how you’ve decided to explain your problem, it sometimes makes sense to solve what could look like an authorization problem within the authentication adapter. With that disclaimer out of the way, ZendAuthenticationAdapterDbTable has some built in mechanisms that can be leveraged for additional checks at authentication time to solve some common user problems. 1 use ZendAuthenticationAdapterDbTable as AuthAdapter; 2 3 4 5 6 7 8 9 // The status field value of an account is not equal to "compromised" $adapter = new AuthAdapter($db, ’users’, ’username’, ’password’, ’MD5(?) AND status != "compromised"’ ); 10 11 // The active field value of an account is equal to "TRUE" 128 Chapter 27. Database Table Authentication
  • 167. Zend Framework 2 Documentation, Release 2.2.5 12 13 14 15 16 17 $adapter = new AuthAdapter($db, ’users’, ’username’, ’password’, ’MD5(?) AND active = "TRUE"’ ); Another scenario can be the implementation of a salting mechanism. Salting is a term referring to a technique which can highly improve your application’s security. It’s based on the idea that concatenating a random string to every password makes it impossible to accomplish a successful brute force attack on the database using pre-computed hash values from a dictionary. Therefore, we need to modify our table to store our salt string: 1 2 3 $sqlAlter = "ALTER TABLE [users] " . "ADD COLUMN [password_salt] " . "AFTER [password]"; Here’s a simple way to generate a salt string for every user at registration: 1 2 3 4 $dynamicSalt = ’’; for ($i = 0; $i < 50; $i++) { $dynamicSalt .= chr(rand(33, 126)); } And now let’s build the adapter: 1 2 3 4 5 6 $adapter = new AuthAdapter($db, ’users’, ’username’, ’password’, "MD5(CONCAT(’staticSalt’, ?, password_salt))" ); Note: You can improve security even more by using a static salt value hard coded into your application. In the case that your database is compromised (e. g. by an SQL injection attack) but your web server is intact your data is still unusable for the attacker. Another alternative is to use the getDbSelect() method of the ZendAuthenticationAdapterDbTable after the adapter has been constructed. This method will return the ZendDbSqlSelect object instance it will use to complete the authenticate() routine. It is important to note that this method will always return the same object regardless if authenticate() has been called or not. This object will not have any of the identity or credential information in it as those values are placed into the select object at authenticate() time. An example of a situation where one might want to use the getDbSelect() method would check the status of a user, in other words to see if that user’s account is enabled. 1 2 3 4 5 6 7 // Continuing with the example from above $adapter = new AuthAdapter($db, ’users’, ’username’, ’password’, ’MD5(?)’ ); 8 9 10 11 // get select object (by reference) $select = $adapter->getDbSelect(); $select->where(’active = "TRUE"’); 27.3. Advanced Usage: Persisting a DbTable Result Object 129
  • 168. Zend Framework 2 Documentation, Release 2.2.5 12 13 14 // authenticate, this ensures that users.active = TRUE $adapter->authenticate(); 130 Chapter 27. Database Table Authentication
  • 169. CHAPTER 28 Digest Authentication 28.1 Introduction Digest authentication is a method of HTTP authentication that improves upon Basic authentication by providing a way to authenticate without having to transmit the password in clear text across the network. This adapter allows authentication against text files containing lines having the basic elements of Digest authentication: • username, such as “joe.user“ • realm, such as “Administrative Area“ • MD5 hash of the username, realm, and password, separated by colons The above elements are separated by colons, as in the following example (in which the password is “somePassword”): 1 someUser:Some Realm:fde17b91c3a510ecbaf7dbd37f59d4f8 28.2 Specifics The digest authentication adapter, ZendAuthenticationAdapterDigest, requires several input parameters: • filename - Filename against which authentication queries are performed • realm - Digest authentication realm • username - Digest authentication user • password - Password for the user of the realm These parameters must be set prior to calling authenticate(). 28.3 Identity The digest authentication adapter returns a ZendAuthenticationResult object, which has been populated with the identity as an array having keys of realm and username. The respective array values associated with these keys correspond to the values set before authenticate() is called. 131
  • 170. Zend Framework 2 Documentation, Release 2.2.5 1 use ZendAuthenticationAdapterDigest as AuthAdapter; 2 3 4 5 6 $adapter = new AuthAdapter($filename, $realm, $username, $password); 7 8 $result = $adapter->authenticate(); 9 10 $identity = $result->getIdentity(); 11 12 print_r($identity); 13 14 15 16 17 18 19 20 /* Array ( [realm] => Some Realm [username] => someUser ) */ 132 Chapter 28. Digest Authentication
  • 171. CHAPTER 29 HTTP Authentication Adapter 29.1 Introduction ZendAuthenticationAdapterHttp provides a mostly-compliant implementation of RFC-2617, Basic and Digest HTTP Authentication. Digest authentication is a method of HTTP authentication that improves upon Basic authentication by providing a way to authenticate without having to transmit the password in clear text across the network. Major Features: • Supports both Basic and Digest authentication. • Issues challenges in all supported schemes, so client can respond with any scheme it supports. • Supports proxy authentication. • Includes support for authenticating against text files and provides an interface for authenticating against other sources, such as databases. There are a few notable features of RFC-2617 that are not implemented yet: • Nonce tracking, which would allow for “stale” support, and increased replay attack protection. • Authentication with integrity checking, or “auth-int”. • Authentication-Info HTTP header. 29.2 Design Overview This adapter consists of two sub-components, the HTTP authentication class itself, and the so-called “Resolvers.” The HTTP authentication class encapsulates the logic for carrying out both Basic and Digest authentication. It uses a Resolver to look up a client’s identity in some data store (text file by default), and retrieve the credentials from the data store. The “resolved” credentials are then compared to the values submitted by the client to determine whether authentication is successful. 133
  • 172. Zend Framework 2 Documentation, Release 2.2.5 29.3 Configuration Options The ZendAuthenticationAdapterHttp class requires a configuration array passed to its constructor. There are several configuration options available, and some are required: Table 29.1: Configuration Options Option Required Name acYes cept_schemes realm Yes diYes, when gest_domains accept_schemes contains digest nonce_timeout when Yes, accept_schemes contains digest use_opaqueNo algoNo rithm proxy_authNo Description Determines which authentication schemes the adapter will accept from the client. Must be a space-separated list containing ‘basic’ and/or ‘digest’. Sets the authentication realm; usernames should be unique within a given realm. Space-separated list of URIs for which the same authentication information is valid. The URIs need not all point to the same server. Sets the number of seconds for which the nonce is valid. See notes below. Specifies whether to send the opaque value in the header. True by default. Specified the algorithm. Defaults to MD5, the only supported option (for now). Disabled by default. Enable to perform Proxy authentication, instead of normal origin server authentication. Note: The current implementation of the nonce_timeout has some interesting side effects. This setting is supposed to determine the valid lifetime of a given nonce, or effectively how long a client’s authentication information is accepted. Currently, if it’s set to 3600 (for example), it will cause the adapter to prompt the client for new credentials every hour, on the hour. This will be resolved in a future release, once nonce tracking and stale support are implemented. 29.4 Resolvers The resolver’s job is to take a username and realm, and return some kind of credential value. Basic authentication expects to receive the Base64 encoded version of the user’s password. Digest authentication expects to receive a hash of the user’s username, the realm, and their password (each separated by colons). Currently, the only supported hash algorithm is MD5. ZendAuthenticationAdapterHttp relies on objects implementing ZendAuthenticationAdapterHttpResolverInterface. A text file resolver class is included with this adapter, but any other kind of resolver can be created simply by implementing the resolver interface. 29.4.1 File Resolver The file resolver is a very simple class. It has a single property specifying a filename, which can also be passed to the constructor. Its resolve() method walks through the text file, searching for a line with a matching username and realm. The text file format similar to Apache htpasswd files: 1 <username>:<realm>:<credentials>n Each line consists of three fields - username, realm, and credentials - each separated by a colon. The credentials field is opaque to the file resolver; it simply returns that value as-is to the caller. Therefore, this same file format serves both 134 Chapter 29. HTTP Authentication Adapter
  • 173. Zend Framework 2 Documentation, Release 2.2.5 Basic and Digest authentication. In Basic authentication, the credentials field should be written in clear text. In Digest authentication, it should be the MD5 hash described above. There are two equally easy ways to create a File resolver: 1 2 3 use ZendAuthenticationAdapterHttpFileResolver; $path = ’files/passwd.txt’; $resolver = new FileResolver($path); or 1 2 3 $path = ’files/passwd.txt’; $resolver = new FileResolver(); $resolver->setFile($path); If the given path is empty or not readable, an exception is thrown. 29.5 Basic Usage First, set up an array with the required configuration values: 1 2 3 4 5 6 $config = array( ’accept_schemes’ ’realm’ ’digest_domains’ ’nonce_timeout’ ); => => => => ’basic digest’, ’My Web Site’, ’/members_only /my_account’, 3600, This array will cause the adapter to accept either Basic or Digest authentication, and will require authenticated access to all the areas of the site under /members_only and /my_account. The realm value is usually displayed by the browser in the password dialog box. The nonce_timeout, of course, behaves as described above. Next, create the ZendAuthenticationAdapterHttp object: 1 $adapter = new ZendAuthenticationAdapterHttp($config); Since we’re supporting both Basic and Digest authentication, we need two different resolver objects. Note that this could just as easily be two different classes: 1 use ZendAuthenticationAdapterHttpFileResolver; 2 3 4 $basicResolver = new FileResolver(); $basicResolver->setFile(’files/basicPasswd.txt’); 5 6 7 $digestResolver = new FileResolver(); $digestResolver->setFile(’files/digestPasswd.txt’); 8 9 10 $adapter->setBasicResolver($basicResolver); $adapter->setDigestResolver($digestResolver); Finally, we perform the authentication. The adapter needs a reference to both the Request and Response objects in order to do its job: 1 2 assert($request instanceof ZendHttpRequest); assert($response instanceof ZendHttpResponse); 3 4 5 $adapter->setRequest($request); $adapter->setResponse($response); 29.5. Basic Usage 135
  • 174. Zend Framework 2 Documentation, Release 2.2.5 6 7 8 9 10 $result = $adapter->authenticate(); if (!$result->isValid()) { // Bad username/password, or canceled password prompt } 136 Chapter 29. HTTP Authentication Adapter
  • 175. CHAPTER 30 LDAP Authentication 30.1 Introduction ZendAuthenticationAdapterLdap supports web application authentication with LDAP services. Its features include username and domain name canonicalization, multi-domain authentication, and failover capabilities. It has been tested to work with Microsoft Active Directory and OpenLDAP, but it should also work with other LDAP service providers. This documentation includes a guide on using ZendAuthenticationAdapterLdap, an exploration of its API, an outline of the various available options, diagnostic information for troubleshooting authentication problems, and example options for both Active Directory and OpenLDAP servers. 30.2 Usage To incorporate ZendAuthenticationAdapterLdap authentication into your application quickly, even if you’re not using ZendMvc, the meat of your code should look something like the following: 1 2 3 4 5 6 7 use use use use use use use ZendAuthenticationAuthenticationService; ZendAuthenticationAdapterLdap as AuthAdapter; ZendConfigReaderIni as ConfigReader; ZendConfigConfig; ZendLogLogger; ZendLogWriterStream as LogWriter; ZendLogFilterPriority as LogFilter; 8 9 10 $username = $this->getRequest()->getPost(’username’); $password = $this->getRequest()->getPost(’password’); 11 12 13 $auth = new AuthenticationService(); 14 15 16 17 $configReader = new ConfigReader(); $configData = $configReader->fromFile(’./ldap-config.ini’); $config = new Config($configData, true); 18 19 20 $log_path = $config->production->ldap->log_path; $options = $config->production->ldap->toArray(); 137
  • 176. Zend Framework 2 Documentation, Release 2.2.5 21 unset($options[’log_path’]); 22 23 24 25 $adapter = new AuthAdapter($options, $username, $password); 26 27 $result = $auth->authenticate($adapter); 28 29 30 if ($log_path) { $messages = $result->getMessages(); 31 $logger = new Logger; $writer = new LogWriter($log_path); 32 33 34 $logger->addWriter($writer); 35 36 $filter = new LogFilter(Logger::DEBUG); $writer->addFilter($filter); 37 38 39 foreach ($messages as $i => $message) { if ($i-- > 1) { // $messages[2] and up are log messages $message = str_replace("n", "n ", $message); $logger->debug("Ldap: $i: $message"); } } 40 41 42 43 44 45 46 } Of course, the logging code is optional, but it is highly recommended that you use a logger. ZendAuthenticationAdapterLdap will record just about every bit of information anyone could want in $messages (more below), which is a nice feature in itself for something that has a history of being notoriously difficult to debug. The ZendConfigReaderIni code is used above to load the adapter options. It is also optional. A regular array would work equally well. The following is an example ldap-config.ini file that has options for two separate servers. With multiple sets of server options the adapter will try each, in order, until the credentials are successfully authenticated. The names of the servers (e.g., ‘server1’ and ‘server2’) are largely arbitrary. For details regarding the options array, see the Server Options section below. Note that ZendConfigReaderIni requires that any values with “equals” characters (=) will need to be quoted (like the DNs shown below). 1 [production] 2 3 ldap.log_path = /tmp/ldap.log 4 5 6 7 8 9 10 11 12 13 ; Typical options for OpenLDAP ldap.server1.host = s0.foo.net ldap.server1.accountDomainName = foo.net ldap.server1.accountDomainNameShort = FOO ldap.server1.accountCanonicalForm = 3 ldap.server1.username = "CN=user1,DC=foo,DC=net" ldap.server1.password = pass1 ldap.server1.baseDn = "OU=Sales,DC=foo,DC=net" ldap.server1.bindRequiresDn = true 14 15 16 17 18 19 ; Typical options for Active Directory ldap.server2.host = dc1.w.net ldap.server2.useStartTls = true ldap.server2.accountDomainName = w.net ldap.server2.accountDomainNameShort = W 138 Chapter 30. LDAP Authentication
  • 177. Zend Framework 2 Documentation, Release 2.2.5 20 21 ldap.server2.accountCanonicalForm = 3 ldap.server2.baseDn = "CN=Users,DC=w,DC=net" The above configuration will instruct ZendAuthenticationAdapterLdap to attempt to authenticate users with the OpenLDAP server s0.foo.net first. If the authentication fails for any reason, the AD server dc1.w.net will be tried. With servers in different domains, this configuration illustrates multi-domain authentication. You can also have multiple servers in the same domain to provide redundancy. Note that in this case, even though OpenLDAP has no need for the short NetBIOS style domain name used by Windows, we provide it here for name canonicalization purposes (described in the Username Canonicalization section below). 30.3 The API The ZendAuthenticationAdapterLdap constructor accepts three parameters. The $options parameter is required and must be an array containing one or more sets of options. Note that it is an array of arrays of ZendLdapLdap options. Even if you will be using only one LDAP server, the options must still be within another array. Below is print_r() output of an example options parameter containing two sets of server options for LDAP servers s0.foo.net and dc1.w.net (the same options as the above INI representation): 1 2 3 4 5 6 7 8 9 10 11 Array ( [server2] => Array ( [host] => dc1.w.net [useStartTls] => 1 [accountDomainName] => w.net [accountDomainNameShort] => W [accountCanonicalForm] => 3 [baseDn] => CN=Users,DC=w,DC=net ) 12 [server1] => Array ( [host] => s0.foo.net [accountDomainName] => foo.net [accountDomainNameShort] => FOO [accountCanonicalForm] => 3 [username] => CN=user1,DC=foo,DC=net [password] => pass1 [baseDn] => OU=Sales,DC=foo,DC=net [bindRequiresDn] => 1 ) 13 14 15 16 17 18 19 20 21 22 23 24 25 ) The information provided in each set of options above is different mainly because AD does not require a username be in DN form when binding (see the bindRequiresDn option in the Server Options section below), which means we can omit a number of options associated with retrieving the DN for a username being authenticated. Note: What is a Distinguished Name? 30.3. The API 139
  • 178. Zend Framework 2 Documentation, Release 2.2.5 A DN or “distinguished name” is a string that represents the path to an object within the LDAP directory. Each comma-separated component is an attribute and value representing a node. The components are evaluated in reverse. For example, the user account CN=Bob Carter,CN=Users,DC=w,DC=net is located directly within the CN=Users,DC=w,DC=net container. This structure is best explored with an LDAP browser like the ADSI Edit MMC snap-in for Active Directory or phpLDAPadmin. The names of servers (e.g. ‘server1’ and ‘server2’ shown above) are largely arbitrary, but for the sake of using ZendConfigReaderIni, the identifiers should be present (as opposed to being numeric indexes) and should not contain any special characters used by the associated file formats (e.g. the ‘.‘INI property separator, ‘&‘ for XML entity references, etc). With multiple sets of server options, the adapter can authenticate users in multiple domains and provide failover so that if one server is not available, another will be queried. Note: The Gory Details: What Happens in the Authenticate Method? When the authenticate() method is called, the adapter iterates over each set of server options, sets them on the internal ZendLdapLdap instance, and calls the ZendLdapLdap::bind() method with the username and password being authenticated. The ZendLdapLdap class checks to see if the username is qualified with a domain (e.g., has a domain component like [email protected] or FOOalice). If a domain is present, but does not match either of the server’s domain names (foo.net or FOO), a special exception is thrown and caught by ZendAuthenticationAdapterLdap that causes that server to be ignored and the next set of server options is selected. If a domain does match, or if the user did not supply a qualified username, ZendLdapLdap proceeds to try to bind with the supplied credentials. if the bind is not successful, ZendLdapLdap throws a ZendLdapExceptionLdapException which is caught by ZendAuthenticationAdapterLdap and the next set of server options is tried. If the bind is successful, the iteration stops, and the adapter’s authenticate() method returns a successful result. If all server options have been tried without success, the authentication fails, and authenticate() returns a failure result with error messages from the last iteration. The username and password parameters of the ZendAuthenticationAdapterLdap constructor represent the credentials being authenticated (i.e., the credentials supplied by the user through your HTML login form). Alternatively, they may also be set with the setUsername() and setPassword() methods. 30.4 Server Options Each set of server options in the context of ZendAuthenticationAdapterLdap consists of the following options, which are passed, largely unmodified, to ZendLdapLdap::setOptions(): 140 Chapter 30. LDAP Authentication
  • 179. Zend Framework 2 Documentation, Release 2.2.5 Table 30.1: Server Options Name host port Description The hostname of LDAP server that these options represent. This option is required. The port on which the LDAP server is listening. If useSsl is TRUE, the default port value is 636. If useSsl is FALSE, the default port value is 389. useStartTls Whether or not the LDAP client should use TLS (aka SSLv2) encrypted transport. A value of TRUE is strongly favored in production environments to prevent passwords from be transmitted in clear text. The default value is FALSE, as servers frequently require that a certificate be installed separately after installation. The useSsl and useStartTls options are mutually exclusive. The useStartTls option should be favored over useSsl but not all servers support this newer mechanism. useSsl Whether or not the LDAP client should use SSL encrypted transport. The useSsl and useStartTls options are mutually exclusive, but useStartTls should be favored if the server and LDAP client library support it. This value also changes the default port value (see port description above). username The DN of the account used to perform account DN lookups. LDAP servers that require the username to be in DN form when performing the “bind” require this option. Meaning, if bindRequiresDn is TRUE, this option is required. This account does not need to be a privileged account; an account with read-only access to objects under the baseDn is all that is necessary (and preferred based on the Principle of Least Privilege). password The password of the account used to perform account DN lookups. If this option is not supplied, the LDAP client will attempt an “anonymous bind” when performing account DN lookups. bindRequiresDn Some LDAP servers require that the username used to bind be in DN form like CN=Alice Baker,OU=Sales,DC=foo,DC=net (basically all servers except AD). If this option is TRUE, this instructs ZendLdapLdap to automatically retrieve the DN corresponding to the username being authenticated, if it is not already in DN form, and then re-bind with the proper DN. The default value is FALSE. Currently only Microsoft Active Directory Server (ADS) is known not to require usernames to be in DN form when binding, and therefore this option may be FALSE with AD (and it should be, as retrieving the DN requires an extra round trip to the server). Otherwise, this option must be set to TRUE (e.g. for OpenLDAP). This option also controls the default accountFilterFormat used when searching for accounts. See the accountFilterFormat option. baseDn The DN under which all accounts being authenticated are located. This option is required. if you are uncertain about the correct baseDn value, it should be sufficient to derive it from the user’s DNS domain using DC= components. For example, if the user’s principal name is [email protected], a baseDn of DC=foo,DC=net should work. A more precise location (e.g., OU=Sales,DC=foo,DC=net) will be more efficient, however. accountCanon- A value of 2, 3 or 4 indicating the form to which account names should be canonicalized after icalForm successful authentication. Values are as follows: 2 for traditional username style names (e.g., alice), 3 for backslash-style names (e.g., FOOalice) or 4 for principal style usernames (e.g., [email protected]). The default value is 4 (e.g., [email protected]). For example, with a value of 3, the identity returned by ZendAuthenticationResult::getIdentity() (and ZendAuthenticationAuthenticationService::getIdentity(), if ZendAuthenticationAuthenticationService was used) will always be FOOalice, regardless of what form Alice supplied, whether it be alice, [email protected], FOOalice, FoOaLicE, foo.netalice, etc. See the Account Name Canonicalization section in the ZendLdapLdap documentation for details. Note that when using multiple sets of server options it is recommended, but not required, that the same accountCanonicalForm be used with all server options so that the resulting usernames are always canonicalized to the same form (e.g., if you canonicalize to EXAMPLEusername with an AD server but to [email protected] with an OpenLDAP server, that may be awkward for the application’s high-level logic). accountDoThe FQDN domain name for which the target LDAP server is an authority (e.g., example.com). mainName This option is used to canonicalize names so that the username supplied by the user can be converted as necessary for binding. It is also used to determine if the server is an authority for the supplied username (e.g., if accountDomainName is foo.net and the user supplies 30.4. Server Options 141 [email protected], the server will not be queried, and a failure will result). This option is not required, but if it is not supplied, usernames in principal name form (e.g., [email protected]) are not supported. It is strongly recommended that you supply this option, as there are many use-cases that require generating the principal name form.
  • 180. Zend Framework 2 Documentation, Release 2.2.5 Note: If you enable useStartTls = TRUE or useSsl = TRUE you may find that the LDAP client generates an error claiming that it cannot validate the server’s certificate. Assuming the PHP LDAP extension is ultimately linked to the OpenLDAP client libraries, to resolve this issue you can set “TLS_REQCERT never” in the OpenLDAP client ldap.conf (and restart the web server) to indicate to the OpenLDAP client library that you trust the server. Alternatively, if you are concerned that the server could be spoofed, you can export the LDAP server’s root certificate and put it on the web server so that the OpenLDAP client can validate the server’s identity. 30.5 Collecting Debugging Messages ZendAuthenticationAdapterLdap collects debugging information within its authenticate() method. This information is stored in the ZendAuthenticationResult object as messages. The array returned by ZendAuthenticationResult::getMessages() is described as follows Table 30.2: Debugging Messages Messages Array Index Index 0 Index 1 Indexes 2 and higher Description A generic, user-friendly message that is suitable for displaying to users (e.g., “Invalid credentials”). If the authentication is successful, this string is empty. A more detailed error message that is not suitable to be displayed to users but should be logged for the benefit of server operators. If the authentication is successful, this string is empty. All log messages in order starting at index 2. In practice, index 0 should be displayed to the user (e.g., using the FlashMessenger helper), index 1 should be logged and, if debugging information is being collected, indexes 2 and higher could be logged as well (although the final message always includes the string from index 1). 30.6 Common Options for Specific Servers 30.6.1 Options for Active Directory For ADS, the following options are noteworthy: 142 Chapter 30. LDAP Authentication
  • 181. Zend Framework 2 Documentation, Release 2.2.5 Table 30.3: Options for Active Directory Name host useStartTls useSsl baseDn accountCanonicalForm accountDomainName accountDomainNameShort Additional Notes As with all servers, this option is required. For the sake of security, this should be TRUE if the server has the necessary certificate installed. Possibly used as an alternative to useStartTls (see above). As with all servers, this option is required. By default AD places all user accounts under the Users container (e.g., CN=Users,DC=foo,DC=net), but the default is not common in larger organizations. Ask your AD administrator what the best DN for accounts for your application would be. You almost certainly want this to be 3 for backslash style names (e.g., FOOalice), which are most familiar to Windows users. You should not use the unqualified form 2 (e.g., alice), as this may grant access to your application to users with the same username in other trusted domains (e.g., BARalice and FOOalice will be treated as the same user). (See also note below.) This is required with AD unless accountCanonicalForm 2 is used, which, again, is discouraged. The NetBIOS name of the domain that users are in and for which the AD server is an authority. This is required if the backslash style accountCanonicalForm is used. Note: Technically there should be no danger of accidental cross-domain authentication with the current ZendAuthenticationAdapterLdap implementation, since server domains are explicitly checked, but this may not be true of a future implementation that discovers the domain at runtime, or if an alternative adapter is used (e.g., Kerberos). In general, account name ambiguity is known to be the source of security issues, so always try to use qualified account names. 30.6.2 Options for OpenLDAP For OpenLDAP or a generic LDAP server using a typical posixAccount style schema, the following options are noteworthy: 30.6. Common Options for Specific Servers 143
  • 182. Zend Framework 2 Documentation, Release 2.2.5 Table 30.4: Options for OpenLDAP Name host useStartTls useSsl username Additional Notes As with all servers, this option is required. For the sake of security, this should be TRUE if the server has the necessary certificate installed. Possibly used as an alternative to useStartTls (see above). Required and must be a DN, as OpenLDAP requires that usernames be in DN form when performing a bind. Try to use an unprivileged account. password The password corresponding to the username above, but this may be omitted if the LDAP server permits an anonymous binding to query user accounts. bindRequiresDn Required and must be TRUE, as OpenLDAP requires that usernames be in DN form when performing a bind. baseDn As with all servers, this option is required and indicates the DN under which all accounts being authenticated are located. accountCanon- Optional, but the default value is 4 (principal style names like [email protected]), which may not icalForm be ideal if your users are used to backslash style names (e.g., FOOalice). For backslash style names use value 3. accountDoRequired unless you’re using accountCanonicalForm 2, which is not recommended. mainName accountDoIf AD is not also being used, this value is not required. Otherwise, if accountCanonicalForm 3 mainis used, this option is required and should be a short name that corresponds adequately to the NameShort accountDomainName (e.g., if your accountDomainName is foo.net, a good accountDomainNameShort value might be FOO). 144 Chapter 30. LDAP Authentication
  • 183. CHAPTER 31 Authentication Validator 31.1 Introduction ZendAuthenticationValidatorAuthentication provides the ability to utilize a validator for an InputFilter in the instance of a Form or for single use where you simply want a true/false value and being able to introspect the error. The available configuration options include: • adapter: This is an instance of ZendAuthenticationAdapter. • identity: This is the identity or name of the identity in the passed in context. • credential: This is the credential or the name of the credential in the passed in context. • service: This is an instance of ZendAuthenticationAuthenticationService 31.2 Basic Usage 1 2 use ZendAuthenticationAuthenticationService; use ZendAuthenticationValidatorAuthentication as AuthenticationValidator; 3 4 5 6 7 8 9 $service = new AuthenticationService(); $adapter = new MyAuthenticationAdapter(); $validator = new AuthenticationValidator( ’service’ => $service, ’adapter’ => $adapter, ); 10 11 12 13 14 $validator->setCredential(’myCredentialContext’); $validator->isValid(’myIdentity’, array( ’myCredentialContext’ => ’myCredential’, )); 145
  • 184. Zend Framework 2 Documentation, Release 2.2.5 146 Chapter 31. Authentication Validator
  • 185. CHAPTER 32 Introduction to ZendBarcode ZendBarcodeBarcode provides a generic way to generate barcodes. The ZendBarcode component is divided into two subcomponents: barcode objects and renderers. Objects allow you to create barcodes independently of the renderer. Renderer allow you to draw barcodes based on the support required. 147
  • 186. Zend Framework 2 Documentation, Release 2.2.5 148 Chapter 32. Introduction to ZendBarcode
  • 187. CHAPTER 33 Barcode creation using ZendBarcodeBarcode class 33.1 Using ZendBarcodeBarcode::factory ZendBarcodeBarcode uses a factory method to create an instance of a renderer that extends ZendBarcodeRendererAbstractRenderer. The factory method accepts five arguments. • The name of the barcode format (e.g., “code39”) or a Traversable object (required) • The name of the renderer (e.g., “image”) (required) • Options to pass to the barcode object (an array or a Traversable object) (optional) • Options to pass to the renderer object (an array or a Traversable object) (optional) • Boolean to indicate whether or not to automatically render errors. If an exception occurs, the provided barcode object will be replaced with an Error representation (optional default TRUE) Getting a Renderer with ZendBarcodeBarcode::factory() ZendBarcodeBarcode::factory() instantiates barcode classes and renderers and ties them together. In this first example, we will use the Code39 barcode type together with the Image renderer. 1 use ZendBarcodeBarcode; 2 3 4 // Only the text to draw is required $barcodeOptions = array(’text’ => ’ZEND-FRAMEWORK’); 5 6 7 8 9 10 // No required options $rendererOptions = array(); $renderer = Barcode::factory( ’code39’, ’image’, $barcodeOptions, $rendererOptions ); 149
  • 188. Zend Framework 2 Documentation, Release 2.2.5 Using ZendBarcodeBarcode::factory() with ZendConfigConfig objects You may pass a ZendConfigConfig object to the factory in order to create the necessary objects. The following example is functionally equivalent to the previous. 1 2 use ZendConfigConfig; use ZendBarcodeBarcode; 3 4 5 6 7 8 9 10 // Using only one ZendConfigConfig object $config = new Config(array( ’barcode’ => ’code39’, ’barcodeParams’ => array(’text’ => ’ZEND-FRAMEWORK’), ’renderer’ => ’image’, ’rendererParams’ => array(’imageType’ => ’gif’), )); 11 12 $renderer = Barcode::factory($config); 33.2 Drawing a barcode When you draw the barcode, you retrieve the resource in which the barcode is drawn. To draw a barcode, you can call the draw() of the renderer, or simply use the proxy method provided by ZendBarcodeBarcode. Drawing a barcode with the renderer object 1 use ZendBarcodeBarcode; 2 3 4 // Only the text to draw is required $barcodeOptions = array(’text’ => ’ZEND-FRAMEWORK’); 5 6 7 // No required options $rendererOptions = array(); 8 9 10 11 12 // Draw the barcode in a new image, $imageResource = Barcode::factory( ’code39’, ’image’, $barcodeOptions, $rendererOptions )->draw(); Drawing a barcode with ZendBarcodeBarcode::draw() 1 use ZendBarcodeBarcode; 2 3 4 // Only the text to draw is required $barcodeOptions = array(’text’ => ’ZEND-FRAMEWORK’); 5 6 7 // No required options $rendererOptions = array(); 8 9 10 11 12 // Draw the barcode in a new image, $imageResource = Barcode::draw( ’code39’, ’image’, $barcodeOptions, $rendererOptions ); 150 Chapter 33. Barcode creation using ZendBarcodeBarcode class
  • 189. Zend Framework 2 Documentation, Release 2.2.5 33.3 Rendering a barcode When you render a barcode, you draw the barcode, you send the headers and you send the resource (e.g. to a browser). To render a barcode, you can call the render() method of the renderer or simply use the proxy method provided by ZendBarcodeBarcode. Rendering a barcode with the renderer object 1 use ZendBarcodeBarcode; 2 3 4 // Only the text to draw is required $barcodeOptions = array(’text’ => ’ZEND-FRAMEWORK’); 5 6 7 // No required options $rendererOptions = array(); 8 9 10 11 12 13 // Draw the barcode in a new image, // send the headers and the image Barcode::factory( ’code39’, ’image’, $barcodeOptions, $rendererOptions )->render(); This will generate this barcode: Rendering a barcode with ZendBarcodeBarcode::render() 1 use ZendBarcodeBarcode; 2 3 4 // Only the text to draw is required $barcodeOptions = array(’text’ => ’ZEND-FRAMEWORK’); 5 6 7 // No required options $rendererOptions = array(); 8 9 10 11 12 13 // Draw the barcode in a new image, // send the headers and the image Barcode::render( ’code39’, ’image’, $barcodeOptions, $rendererOptions ); This will generate the same barcode as the previous example. 33.3. Rendering a barcode 151
  • 190. Zend Framework 2 Documentation, Release 2.2.5 152 Chapter 33. Barcode creation using ZendBarcodeBarcode class
  • 191. CHAPTER 34 ZendBarcodeBarcode Objects Barcode objects allow you to generate barcodes independently of the rendering support. After generation, you can retrieve the barcode as an array of drawing instructions that you can provide to a renderer. Objects have a large number of options. Most of them are common to all objects. These options can be set in three ways: • As an array or a Traversable object passed to the constructor. • As an array passed to the setOptions() method. • Via individual setters for each configuration type. Different ways to parameterize a barcode object 1 use ZendBarcodeObject; 2 3 $options = array(’text’ => ’ZEND-FRAMEWORK’, ’barHeight’ => 40); 4 5 6 // Case 1: constructor $barcode = new ObjectCode39($options); 7 8 9 10 // Case 2: setOptions() $barcode = new ObjectCode39(); $barcode->setOptions($options); 11 12 13 14 15 // Case 3: individual setters $barcode = new ObjectCode39(); $barcode->setText(’ZEND-FRAMEWORK’) ->setBarHeight(40); 34.1 Common Options In the following list, the values have no units; we will use the term “unit.” For example, the default value of the “thin bar” is “1 unit”. The real units depend on the rendering support (see the renderers documentation for more information). Setters are each named by uppercasing the initial letter of the option and prefixing the name with “set” (e.g. “barHeight” becomes “setBarHeight”). All options have a corresponding getter prefixed with “get” (e.g. “getBarHeight”). Available options are: 153
  • 192. Zend Framework 2 Documentation, Release 2.2.5 Table 34.1: Common Options Option Integer Integer Default Description Value ZendBarcodeObject Namespace of the barcode; for example, if you need to extend the embedding objects 50 Height of the bars 3 Width of the thick bar Integer 1 Width of the thin bar Integer 1 foreColor Integer backgroundColor orientation font Integer or String Float String or Integer Float Boolean Boolean 0x000000 (black) 0xFFFFFF (white) 0 NULL Factor by which to multiply bar widths and font sizes (barHeight, barThinWidth, barThickWidth and fontSize) Color of the bar and the text. Could be provided as an integer or as a HTML value (e.g. “#333333”) Color of the background. Could be provided as an integer or as a HTML value (e.g. “#333333”) Orientation of the barcode Font path to a TTF font or a number between 1 and 5 if using image generation with GD (internal fonts) Size of the font (not applicable with numeric fonts) Draw a border around the barcode and the quiet zones Leave a quiet zone before and after the barcode barcodeNamespace barHeight barThickWidth barThinWidth factor fontSize withBorder withQuietZones drawText stretchText withChecksum withChecksumInText text Data Type String 10 FALSE TRUE Boolean Boolean Boolean TRUE FALSE FALSE Boolean FALSE String NULL Set if the text is displayed below the barcode Specify if the text is stretched all along the barcode Indicate whether or not the checksum is automatically added to the barcode Indicate whether or not the checksum is displayed in the textual representation The text to represent as a barcode 34.1.1 Particular case of static setBarcodeFont() You can set a common font for all your objects by using the static method ZendBarcodeBarcode::setBarcodeFont(). This value can be always be overridden for individual objects by using the setFont() method. 1 use ZendBarcodeBarcode; 2 3 4 // In your bootstrap: Barcode::setBarcodeFont(’my_font.ttf’); 5 6 7 8 9 10 11 // Later in your code: Barcode::render( ’code39’, ’pdf’, array(’text’ => ’ZEND-FRAMEWORK’) ); // will use ’my_font.ttf’ 12 13 14 15 16 // or: Barcode::render( ’code39’, ’image’, 154 Chapter 34. ZendBarcodeBarcode Objects
  • 193. Zend Framework 2 Documentation, Release 2.2.5 17 18 19 20 21 array( ’text’ => ’ZEND-FRAMEWORK’, ’font’ => 3 ) ); // will use the 3rd GD internal font 34.2 Common Additional Getters Table 34.2: Common Getters Getter getType() Data Type String Description getRawText() getTextToDisplay() getQuietZone() String String Integer getInstructions() getHeight($recalculate = false) getWidth($recalculate = false) getOffsetTop($recalculate = false) getOffsetLeft($recalculate = false) Array Integer Return the name of the barcode class without the namespace (e.g. ZendBarcodeObjectCode39 returns simply “code39”) Return the original text provided to the object Return the text to display, including, if activated, the checksum value Return the size of the space needed before and after the barcode without any drawing Return drawing instructions as an array. Return the height of the barcode calculated after possible rotation Integer Return the width of the barcode calculated after possible rotation Integer Return the position of the top of the barcode calculated after possible rotation Integer Return the position of the left of the barcode calculated after possible rotation 34.3 Description of shipped barcodes You will find below detailed information about all barcode types shipped by default with Zend Framework. 34.3.1 ZendBarcodeObjectError This barcode is a special case. It is internally used to automatically render an exception caught by the ZendBarcode component. 34.2. Common Additional Getters 155
  • 194. Zend Framework 2 Documentation, Release 2.2.5 34.3.2 ZendBarcodeObjectCode128 • Name: Code 128 • Allowed characters: the complete ASCII-character set • Checksum: optional (modulo 103) • Length: variable There are no particular options for this barcode. 34.3.3 ZendBarcodeObjectCodabar • Name: Codabar (or Code 2 of 7) • Allowed characters:‘0123456789-$:/.+’ with ‘ABCD’ as start and stop characters • Checksum: none • Length: variable There are no particular options for this barcode. 34.3.4 ZendBarcodeObjectCode25 • Name: Code 25 (or Code 2 of 5 or Code 25 Industrial) • Allowed characters:‘0123456789’ • Checksum: optional (modulo 10) • Length: variable There are no particular options for this barcode. 156 Chapter 34. ZendBarcodeBarcode Objects
  • 195. Zend Framework 2 Documentation, Release 2.2.5 34.3.5 ZendBarcodeObjectCode25interleaved This barcode extends ZendBarcodeObjectCode25 (Code 2 of 5), and has the same particulars and options, and adds the following: • Name: Code 2 of 5 Interleaved • Allowed characters:‘0123456789’ • Checksum: optional (modulo 10) • Length: variable (always even number of characters) Available options include: Table 34.3: ZendBarcodeObjectCode25interleaved Options Option withBearerBars Data Type Boolean Default Value FALSE Description Draw a thick bar at the top and the bottom of the barcode. Note: If the number of characters is not even, ZendBarcodeObjectCode25interleaved will automatically prepend the missing zero to the barcode text. 34.3.6 ZendBarcodeObjectEan2 This barcode extends ZendBarcodeObjectEan5 (EAN 5), and has the same particulars and options, and adds the following: • Name: EAN-2 • Allowed characters:‘0123456789’ • Checksum: only use internally but not displayed • Length: 2 characters There are no particular options for this barcode. Note: If the number of characters is lower than 2, ZendBarcodeObjectEan2 will automatically prepend the missing zero to the barcode text. 34.3. Description of shipped barcodes 157
  • 196. Zend Framework 2 Documentation, Release 2.2.5 34.3.7 ZendBarcodeObjectEan5 This barcode extends ZendBarcodeObjectEan13 (EAN 13), and has the same particulars and options, and adds the following: • Name: EAN-5 • Allowed characters:‘0123456789’ • Checksum: only use internally but not displayed • Length: 5 characters There are no particular options for this barcode. Note: If the number of characters is lower than 5, ZendBarcodeObjectEan5 will automatically prepend the missing zero to the barcode text. 34.3.8 ZendBarcodeObjectEan8 This barcode extends ZendBarcodeObjectEan13 (EAN 13), and has the same particulars and options, and adds the following: • Name: EAN-8 • Allowed characters:‘0123456789’ • Checksum: mandatory (modulo 10) • Length: 8 characters (including checksum) There are no particular options for this barcode. Note: If the number of characters is lower than 8, ZendBarcodeObjectEan8 will automatically prepend the missing zero to the barcode text. 158 Chapter 34. ZendBarcodeBarcode Objects
  • 197. Zend Framework 2 Documentation, Release 2.2.5 34.3.9 ZendBarcodeObjectEan13 • Name: EAN-13 • Allowed characters:‘0123456789’ • Checksum: mandatory (modulo 10) • Length: 13 characters (including checksum) There are no particular options for this barcode. Note: If the number of characters is lower than 13, ZendBarcodeObjectEan13 will automatically prepend the missing zero to the barcode text. The option withQuietZones has no effect with this barcode. 34.3.10 ZendBarcodeObjectCode39 • Name: Code 39 • Allowed characters:‘0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ -.$/+%’ • Checksum: optional (modulo 43) • Length: variable Note: ZendBarcodeObjectCode39 will automatically add the start and stop characters (‘*’) for you. There are no particular options for this barcode. 34.3.11 ZendBarcodeObjectIdentcode This barcode extends ZendBarcodeObjectCode25interleaved (Code 2 of 5 Interleaved), and inherits some of its capabilities; it also has a few particulars of its own. • Name: Identcode (Deutsche Post Identcode) 34.3. Description of shipped barcodes 159
  • 198. Zend Framework 2 Documentation, Release 2.2.5 • Allowed characters:‘0123456789’ • Checksum: mandatory (modulo 10 different from Code25) • Length: 12 characters (including checksum) There are no particular options for this barcode. Note: If the number of characters is lower than 12, ZendBarcodeObjectIdentcode will automatically prepend missing zeros to the barcode text. 34.3.12 ZendBarcodeObjectItf14 This barcode extends ZendBarcodeObjectCode25interleaved (Code 2 of 5 Interleaved), and inherits some of its capabilities; it also has a few particulars of its own. • Name: ITF-14 • Allowed characters:‘0123456789’ • Checksum: mandatory (modulo 10) • Length: 14 characters (including checksum) There are no particular options for this barcode. Note: If the number of characters is lower than 14, ZendBarcodeObjectItf14 will automatically prepend missing zeros to the barcode text. 34.3.13 ZendBarcodeObjectLeitcode This barcode extends ZendBarcodeObjectIdentcode (Deutsche Post Identcode), and inherits some of its capabilities; it also has a few particulars of its own. • Name: Leitcode (Deutsche Post Leitcode) • Allowed characters:‘0123456789’ • Checksum: mandatory (modulo 10 different from Code25) • Length: 14 characters (including checksum) 160 Chapter 34. ZendBarcodeBarcode Objects
  • 199. Zend Framework 2 Documentation, Release 2.2.5 There are no particular options for this barcode. Note: If the number of characters is lower than 14, ZendBarcodeObjectLeitcode will automatically prepend missing zeros to the barcode text. 34.3.14 ZendBarcodeObjectPlanet • Name: Planet (PostaL Alpha Numeric Encoding Technique) • Allowed characters:‘0123456789’ • Checksum: mandatory (modulo 10) • Length: 12 or 14 characters (including checksum) There are no particular options for this barcode. 34.3.15 ZendBarcodeObjectPostnet • Name: Postnet (POSTal Numeric Encoding Technique) • Allowed characters:‘0123456789’ • Checksum: mandatory (modulo 10) • Length: 6, 7, 10 or 12 characters (including checksum) There are no particular options for this barcode. 34.3.16 ZendBarcodeObjectRoyalmail • Name: Royal Mail or RM4SCC (Royal Mail 4-State Customer Code) • Allowed characters:‘0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ’ • Checksum: mandatory • Length: variable There are no particular options for this barcode. 34.3. Description of shipped barcodes 161
  • 200. Zend Framework 2 Documentation, Release 2.2.5 34.3.17 ZendBarcodeObjectUpca This barcode extends ZendBarcodeObjectEan13 (EAN-13), and inherits some of its capabilities; it also has a few particulars of its own. • Name: UPC-A (Universal Product Code) • Allowed characters:‘0123456789’ • Checksum: mandatory (modulo 10) • Length: 12 characters (including checksum) There are no particular options for this barcode. Note: If the number of characters is lower than 12, ZendBarcodeObjectUpca will automatically prepend missing zeros to the barcode text. The option withQuietZones has no effect with this barcode. 34.3.18 ZendBarcodeObjectUpce This barcode extends ZendBarcodeObjectUpca (UPC-A), and inherits some of its capabilities; it also has a few particulars of its own. The first character of the text to encode is the system (0 or 1). • Name: UPC-E (Universal Product Code) • Allowed characters:‘0123456789’ • Checksum: mandatory (modulo 10) • Length: 8 characters (including checksum) There are no particular options for this barcode. Note: If the number of characters is lower than 8, ZendBarcodeObjectUpce will automatically prepend missing zeros to the barcode text. Note: If the first character of the text to encode is not 0 or 1, ZendBarcodeObjectUpce will automatically replace it by 0. The option withQuietZones has no effect with this barcode. 162 Chapter 34. ZendBarcodeBarcode Objects
  • 201. CHAPTER 35 ZendBarcode Renderers Renderers have some common options. These options can be set in three ways: • As an array or a Traversable object passed to the constructor. • As an array passed to the setOptions() method. • As discrete values passed to individual setters. Different ways to parameterize a renderer object 1 use ZendBarcodeRenderer; 2 3 $options = array(’topOffset’ => 10); 4 5 6 // Case 1 $renderer = new RendererPdf($options); 7 8 9 10 // Case 2 $renderer = new RendererPdf(); $renderer->setOptions($options); 11 12 13 14 // Case 3 $renderer = new RendererPdf(); $renderer->setTopOffset(10); 35.1 Common Options In the following list, the values have no unit; we will use the term “unit.” For example, the default value of the “thin bar” is “1 unit.” The real units depend on the rendering support. The individual setters are obtained by uppercasing the initial letter of the option and prefixing the name with “set” (e.g. “barHeight” => “setBarHeight”). All options have a correspondent getter prefixed with “get” (e.g. “getBarHeight”). Available options are: 163
  • 202. Zend Framework 2 Documentation, Release 2.2.5 Table 35.1: Common Options Option Data Type String Default Description Value ZendBarcodeRenderer of the renderer; for example, if you need to extend the Namespace renderers String “left” Can be “left”, “center” or “right”. Can be useful with PDF or if the setWidth() method is used with an image renderer. String “top” Integer 0 topOffset Integer 0 automaticRenderError moduleSize barcode Boolean FALSE Float 1 Can be “top”, “middle” or “bottom”. Can be useful with PDF or if the setHeight() method is used with an image renderer. Top position of the barcode inside the renderer. If used, this value will override the “horizontalPosition” option. Top position of the barcode inside the renderer. If used, this value will override the “verticalPosition” option. Whether or not to automatically render errors. If an exception occurs, the provided barcode object will be replaced with an Error representation. Note that some errors (or exceptions) can not be rendered. Size of a rendering module in the support. rendererNamespace horizontalPosition verticalPosition leftOffset ZendBarcodeObject NULL The barcode object to render. An additional getter exists: getType(). It returns the name of the renderer class without the namespace (e.g. ZendBarcodeRendererImage returns “image”). 35.2 ZendBarcodeRendererImage The Image renderer will draw the instruction list of the barcode object in an image resource. The component requires the GD extension. The default width of a module is 1 pixel. Available options are: Table 35.2: ZendBarcodeRendererImage Options Option height Data Type Integer Default Value 0 width Integer 0 imageType String “png” Description Allow you to specify the height of the result image. If “0”, the height will be calculated by the barcode object. Allow you to specify the width of the result image. If “0”, the width will be calculated by the barcode object. Specify the image format. Can be “png”, “jpeg”, “jpg” or “gif”. 35.3 ZendBarcodeRendererPdf The PDF renderer will draw the instruction list of the barcode object in a PDF document. The default width of a module is 0.5 point. There are no particular options for this renderer. 164 Chapter 35. ZendBarcode Renderers
  • 203. CHAPTER 36 ZendCacheStorageAdapter 36.1 Overview Storage adapters are wrappers for real storage resources such as memory and the filesystem, using the well known adapter pattern. They come with tons of methods to read, write and modify stored items and to get information about stored items and the storage. All adapters implement the interface ZendCacheStorageStorageInterface and most extend ZendCacheStorageAdapterAbstractAdapter, which comes with basic logic. Configuration is handled by either ZendCacheStorageAdapterAdapterOptions, or an adapter-specific options class if it exists. You may pass the options instance to the class at instantiation or via the setOptions() method, or alternately pass an associative array of options in either place (internally, these are then passed to an options class instance). Alternately, you can pass either the options instance or associative array to the ZendCacheStorageFactory::factory method. Note: Many methods throw exceptions Because many caching operations throw an exception on error, you need to catch them manually or you can use the plug-in ZendCacheStoragePluginExceptionHandler with throw_exceptions set to false to automatically catch them. You can also define an exception_callback to log exceptions. 36.2 Quick Start Caching adapters can either be created from the provided ZendCacheStorageFactory factory, or by simply instantiating one of the ZendCacheStorageAdapter* classes. To make life easier, the ZendCacheStorageFactory comes with a factory method to create an adapter and create/add all requested plugins at once. 1 use ZendCacheStorageFactory; 2 3 4 5 // Via factory: $cache = StorageFactory::factory(array( ’adapter’ => array( 165
  • 204. Zend Framework 2 Documentation, Release 2.2.5 ’name’ => ’apc’, ’options’ => array(’ttl’ => 3600), 6 7 ), ’plugins’ => array( ’exception_handler’ => array(’throw_exceptions’ => false), ), 8 9 10 11 12 )); 13 14 15 16 17 18 19 // Alternately: $cache = StorageFactory::adapterFactory(’apc’, array(’ttl’ => 3600)); $plugin = StorageFactory::pluginFactory(’exception_handler’, array( ’throw_exceptions’ => false, )); $cache->addPlugin($plugin); 20 21 22 23 // Or manually: $cache = new ZendCacheStorageAdapterApc(); $cache->getOptions()->setTtl(3600); 24 25 26 27 $plugin = new ZendCacheStoragePluginExceptionHandler(); $plugin->getOptions()->setThrowExceptions(false); $cache->addPlugin($plugin); 36.3 Basic Configuration Options Basic configuration is handled by either ZendCacheStorageAdapterAdapterOptions, or an adapter-specific options class if it exists. You may pass the options instance to the class at instantiation or via the setOptions() method, or alternately pass an associative array of options in either place (internally, these are then passed to an options class instance). Alternately, you can pass either the options instance or associative array to the ZendCacheStorageFactory::factory method. The following configuration options are defined by ZendCacheStorageAdapterAdapterOptions and are available for every supported adapter. Adapter-specific configuration options are described on adapter level below. Option ttl namespace key_pattern readable writable Data Type integer string null‘‘|‘‘string boolean boolean Default Value 0 “zfcache” null true true Description Time to live The “namespace” in which cache items will live Pattern against which to validate cache keys Enable/Disable reading data from cache Enable/Disable writing data to cache 36.4 The StorageInterface The ZendCacheStorageStorageInterface is the basic interface implemented by all storage adapters. getItem(string $key, boolean & $success = null, mixed & $casToken = null) Load an item with the given $key. If item exists set parameter $success to true, set parameter $casToken and returns mixed value of item. If item can’t load set parameter $success to false and returns null. Return type mixed 166 Chapter 36. ZendCacheStorageAdapter
  • 205. Zend Framework 2 Documentation, Release 2.2.5 getItems(array $keys) Load all items given by $keys returning key-value pairs. Return type array hasItem(string $key) Test if an item exists. Return type boolean hasItems(array $keys) Test multiple items. Return type string[] getMetadata(string $key) Get metadata of an item. Return type array|boolean getMetadatas(array $keys) Get multiple metadata. Return type array setItem(string $key, mixed $value) Store an item. Return type boolean setItems(array $keyValuePairs) Store multiple items. Return type boolean addItem(string $key, mixed $value) Add an item. Return type boolean addItems(array $keyValuePairs) Add multiple items. Return type boolean replaceItem(string $key, mixed $value) Replace an item. Return type boolean replaceItems(array $keyValuePairs) Replace multiple items. Return type boolean checkAndSetItem(mixed $token, string $key, mixed $value) Set item only if token matches. It uses the token received from getItem() to check if the item has changed before overwriting it. Return type boolean touchItem(string $key) Reset lifetime of an item. Return type boolean 36.4. The StorageInterface 167
  • 206. Zend Framework 2 Documentation, Release 2.2.5 touchItems(array $keys) Reset lifetime of multiple items. Return type boolean removeItem(string $key) Remove an item. Return type boolean removeItems(array $keys) Remove multiple items. Return type boolean incrementItem(string $key, int $value) Increment an item. Return type integer|boolean incrementItems(array $keyValuePairs) Increment multiple items. Return type boolean decrementItem(string $key, int $value) Decrement an item. Return type integer|boolean decrementItems(array $keyValuePairs) Decrement multiple items. Return type boolean getCapabilities() Capabilities of this storage. Return type ZendCacheStorageCapabilities 36.5 The AvailableSpaceCapableInterface The ZendCacheStorageAvailableSpaceCapableInterface implements a method to make it possible getting the current available space of the storage. getAvailableSpace() Get available space in bytes. Return type integer|float 36.6 The TotalSpaceCapableInterface The ZendCacheStorageTotalSpaceCapableInterface implements a method to make it possible getting the total space of the storage. getTotalSpace() Get total space in bytes. Return type integer|float 168 Chapter 36. ZendCacheStorageAdapter
  • 207. Zend Framework 2 Documentation, Release 2.2.5 36.7 The ClearByNamespaceInterface The ZendCacheStorageClearByNamespaceInterface implements a method to clear all items of a given namespace. clearByNamespace(string $namespace) Remove items of given namespace. Return type boolean 36.8 The ClearByPrefixInterface The ZendCacheStorageClearByPrefixInterface implements a method to clear all items of a given prefix (within the current configured namespace). clearByPrefix(string $prefix) Remove items matching given prefix. Return type boolean 36.9 The ClearExpiredInterface The ZendCacheStorageClearExpiredInterface implements a method to clear all expired items (within the current configured namespace). clearExpired() Remove expired items. Return type boolean 36.10 The FlushableInterface The ZendCacheStorageFlushableInterface implements a method to flush the complete storage. flush() Flush the whole storage. Return type boolean 36.11 The IterableInterface The ZendCacheStorageIterableInterface implements a method to get an iterator to iterate over items of the storage. It extends IteratorAggregate so it’s possible to directly iterate over the storage using foreach. getIterator() Get an Iterator. Return type ZendCacheStorageIteratorInterface 36.7. The ClearByNamespaceInterface 169
  • 208. Zend Framework 2 Documentation, Release 2.2.5 36.12 The OptimizableInterface The ZendCacheStorageOptimizableInterface implements a method to run optimization processes on the storage. optimize() Optimize the storage. Return type boolean 36.13 The TaggableInterface The ZendCacheStorageTaggableInterface implements methods to mark items with one or more tags and to clean items matching tags. setTags(string $key, string[] $tags) Set tags to an item by given key. (An empty array will remove all tags) Return type boolean getTags(string $key) Get tags of an item by given key. Return type string[]|false clearByTags(string[] $tags, boolean $disjunction = false) Remove items matching given tags. If $disjunction is true only one of the given tags must match else all given tags must match. Return type boolean 36.14 The Apc Adapter The ZendCacheStorageAdapterApc adapter stores cache items in shared memory through the required PHP extension APC (Alternative PHP Cache). This adapter implements the following interfaces: • ZendCacheStorageStorageInterface • ZendCacheStorageAvailableSpaceCapableInterface • ZendCacheStorageClearByNamespaceInterface • ZendCacheStorageClearByPrefixInterface • ZendCacheStorageFlushableInterface • ZendCacheStorageIterableInterface • ZendCacheStorageTotalSpaceCapableInterface 170 Chapter 36. ZendCacheStorageAdapter
  • 209. Zend Framework 2 Documentation, Release 2.2.5 Table 36.1: Capabilities Capability supportedDatatypes supportedMetadata minTtl maxTtl staticTtl ttlPrecision useRequestTime expiredRead maxKeyLength namespaceIsPrefix namespaceSeparator Value null, boolean, integer, double, string, array (serialized), object (serialized) internal_key, atime, ctime, mtime, rtime, size, hits, ttl 1 0 true 1 <ini value of apc.use_request_time> false 5182 true <Option value of namespace_separator> Table 36.2: Adapter specific options Name namespace_separator Data Type string Default Value ”:” Description A separator for the namespace and prefix 36.15 The Dba Adapter The ZendCacheStorageAdapterDba adapter stores cache items into dbm like databases using the required PHP extension dba. This adapter implements the following interfaces: • ZendCacheStorageStorageInterface • ZendCacheStorageAvailableSpaceCapableInterface • ZendCacheStorageClearByNamespaceInterface • ZendCacheStorageClearByPrefixInterface • ZendCacheStorageFlushableInterface • ZendCacheStorageIterableInterface • ZendCacheStorageOptimizableInterface • ZendCacheStorageTotalSpaceCapableInterface Table 36.3: Capabilities Capability supportedDatatypes supportedMetadata minTtl maxKeyLength namespaceIsPrefix namespaceSeparator Value string, null => string, boolean => string, integer => string, double => string <none> 0 0 true <Option value of namespace_separator> 36.15. The Dba Adapter 171
  • 210. Zend Framework 2 Documentation, Release 2.2.5 Table 36.4: Adapter specific options Name handler Default Value ”:” string string “” “c” string namespace_separator pathname mode Data Type string “flatfile” Description A separator for the namespace and prefix Pathname to the database file The mode to open the database Please read dba_open for more information The name of the handler which shall be used for accessing the database. Note: This adapter doesn’t support automatically expire items Because of this adapter doesn’t support automatically expire items it’s very important to clean outdated items by self. 36.16 The Filesystem Adapter The ZendCacheStorageAdapterFilesystem adapter stores cache items into the filesystem. This adapter implements the following interfaces: • ZendCacheStorageStorageInterface • ZendCacheStorageAvailableSpaceCapableInterface • ZendCacheStorageClearByNamespaceInterface • ZendCacheStorageClearByPrefixInterface • ZendCacheStorageClearExpiredInterface • ZendCacheStorageFlushableInterface • ZendCacheStorageIterableInterface • ZendCacheStorageOptimizableInterface • ZendCacheStorageTaggableInterface • ZendCacheStorageTotalSpaceCapableInterface 172 Chapter 36. ZendCacheStorageAdapter
  • 211. Zend Framework 2 Documentation, Release 2.2.5 Table 36.5: Capabilities Capability supportedDatatypes supportedMetadata minTtl maxTtl staticTtl ttlPrecision useRequestTime expiredRead maxKeyLength namespaceIsPrefix namespaceSeparator Value string, null => string, boolean => string, integer => string, double => string mtime, filespec, atime, ctime 1 0 false 1 false true 251 true <Option value of namespace_separator> Table 36.6: Adapter specific options Name namespace_separator cache_dir clear_stat_cache dir_level Data Type string Default Value ”:” Description A separator for the namespace and prefix string boolean integer “” true 1 dir_permission integer false boolean integer false string boolean boolean integer false 0700 Directory to store cache files Call clearstatcache() enabled? Defines how much sub-directories should be created Set explicit permission on creating new directories Lock files on writing Set explicit permission on creating new files file_locking file_permission key_pattern no_atime no_ctime umask true 0600 /^[a-z0-9_+-]*$/Di Validate key against pattern true Don’t get ‘fileatime’ as ‘atime’ on metadata true Don’t get ‘filectime’ as ‘ctime’ on metadata false Use umask to set file and directory permissions 36.17 The Memcached Adapter The ZendCacheStorageAdapterMemcached adapter stores cache items over the memcached protocol. It’s using the required PHP extension memcached which is based on Libmemcached. This adapter implements the following interfaces: • ZendCacheStorageStorageInterface • ZendCacheStorageAvailableSpaceCapableInterface • ZendCacheStorageFlushableInterface • ZendCacheStorageTotalSpaceCapableInterface 36.17. The Memcached Adapter 173
  • 212. Zend Framework 2 Documentation, Release 2.2.5 Table 36.7: Capabilities Capability supportedDatatypes supportedMetadata minTtl maxTtl staticTtl ttlPrecision useRequestTime expiredRead maxKeyLength namespaceIsPrefix namespaceSeparator Value null, boolean, integer, double, string, array (serialized), object (serialized) <none> 1 0 true 1 false false 255 true <none> Table 36.8: Adapter specific options Name Data Type Default Value servers array [] lib_options array [] Description List of servers in [] = array(string host, integer port) Associative array of Libmemcached options were the array key is the option name (without the prefix “OPT_”) or the constant value. The array value is the option value Please read this<http://php.net/manual/memcached.setoption.php> for more information 36.18 The Memory Adapter The ZendCacheStorageAdapterMemory adapter stores cache items into the PHP process using an array. This adapter implements the following interfaces: • ZendCacheStorageStorageInterface • ZendCacheStorageAvailableSpaceCapableInterface • ZendCacheStorageClearByPrefixInterface • ZendCacheStorageClearExpiredInterface • ZendCacheStorageFlushableInterface • ZendCacheStorageIterableInterface • ZendCacheStorageTaggableInterface • ZendCacheStorageTotalSpaceCapableInterface 174 Chapter 36. ZendCacheStorageAdapter
  • 213. Zend Framework 2 Documentation, Release 2.2.5 Table 36.9: Capabilities Capability supportedDatatypes supportedMetadata minTtl maxTtl staticTtl ttlPrecision useRequestTime expiredRead maxKeyLength namespaceIsPrefix Value string, null, boolean, integer, double, array, object, resource mtime 1 <Value of PHP_INT_MAX> false 0.05 false true 0 false Table 36.10: Adapter specific options Name memory_limit Data Type string integer Default Value <50% of ini memory_limit> value Description Limit of how much memory can PHP allocate to allow store items into this adapter • If the used memory of PHP exceeds this limit an OutOfSpaceException will be thrown. • A number less or equal 0 will disable the memory limit • When a number is used, the value is measured in bytes (Shorthand notation may also be used) Note: All stored items will be lost after terminating the script. 36.19 The WinCache Adapter The ZendCacheStorageAdapterWinCache adapter stores cache items into shared memory through the required PHP extension WinCache. This adapter implements the following interfaces: • ZendCacheStorageStorageInterface • ZendCacheStorageAvailableSpaceCapableInterface • ZendCacheStorageFlushableInterface • ZendCacheStorageTotalSpaceCapableInterface 36.19. The WinCache Adapter 175
  • 214. Zend Framework 2 Documentation, Release 2.2.5 Table 36.11: Capabilities Capability supportedDatatypes supportedMetadata minTtl maxTtl staticTtl ttlPrecision useRequestTime expiredRead namespaceIsPrefix namespaceSeparator Value null, boolean, integer, double, string, array (serialized), object (serialized) internal_key, ttl, hits, size 1 0 true 1 <ini value of apc.use_request_time> false true <Option value of namespace_separator> Table 36.12: Adapter specific options Name namespace_separator Data Type string Default Value ”:” Description A separator for the namespace and prefix 36.20 The XCache Adapter The ZendCacheStorageAdapterXCache adapter stores cache items into shared memory through the required PHP extension XCache. This adapter implements the following interfaces: • ZendCacheStorageStorageInterface • ZendCacheStorageAvailableSpaceCapableInterface • ZendCacheStorageClearByNamespaceInterface • ZendCacheStorageClearByPrefixInterface • ZendCacheStorageFlushableInterface • ZendCacheStorageIterableInterface • ZendCacheStorageTotalSpaceCapableInterface Table 36.13: Capabilities Capability supportedDatatypes supportedMetadata minTtl maxTtl staticTtl ttlPrecision useRequestTime expiredRead maxKeyLength namespaceIsPrefix namespaceSeparator 176 Value boolean, integer, double, string, array (serialized), object (serialized) internal_key, size, refcount, hits, ctime, atime, hvalue 1 <ini value of xcache.var_maxttl> true 1 true false 5182 true <Option value of namespace_separator> Chapter 36. ZendCacheStorageAdapter
  • 215. Zend Framework 2 Documentation, Release 2.2.5 Table 36.14: Adapter specific options Name Data Type Default Value namesstring”:” pace_separator adbooleanalse f min_auth admin_user admin_pass Description A separator for the namespace and prefix string“” Enable admin authentication by configuration options admin_user and admin_pass This makes XCache administration functions accessible if xcache.admin.enable_auth is enabled without the need of HTTP-Authentication. The username of xcache.admin.user string“” The password of xcache.admin.pass in plain text 36.21 The ZendServerDisk Adapter This ZendCacheStorageAdapterZendServerDisk adapter stores cache items on filesystem through the Zend Server Data Caching API. This adapter implements the following interfaces: • ZendCacheStorageStorageInterface • ZendCacheStorageAvailableSpaceCapableInterface • ZendCacheStorageClearByNamespaceInterface • ZendCacheStorageFlushableInterface • ZendCacheStorageTotalSpaceCapableInterface Table 36.15: Capabilities Capability supportedDatatypes supportedMetadata minTtl maxTtl maxKeyLength staticTtl ttlPrecision useRequestTime expiredRead namespaceIsPrefix namespaceSeparator Value null, boolean, integer, double, string, array (serialized), object (serialized) <none> 1 0 0 true 1 false false true :: 36.22 The ZendServerShm Adapter The ZendCacheStorageAdapterZendServerShm adapter stores cache items in shared memory through the Zend Server Data Caching API. 36.21. The ZendServerDisk Adapter 177
  • 216. Zend Framework 2 Documentation, Release 2.2.5 This adapter implements the following interfaces: • ZendCacheStorageStorageInterface • ZendCacheStorageClearByNamespaceInterface • ZendCacheStorageFlushableInterface • ZendCacheStorageTotalSpaceCapableInterface Table 36.16: Capabilities Capability supportedDatatypes supportedMetadata minTtl maxTtl maxKeyLength staticTtl ttlPrecision useRequestTime expiredRead namespaceIsPrefix namespaceSeparator Value null, boolean, integer, double, string, array (serialized), object (serialized) <none> 1 0 0 true 1 false false true :: 36.23 Examples Basic usage 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $cache = ZendCacheStorageFactory::factory(array( ’adapter’ => array( ’name’ => ’filesystem’ ), ’plugins’ => array( // Don’t throw exceptions on cache errors ’exception_handler’ => array( ’throw_exceptions’ => false ), ) )); $key = ’unique-cache-key’; $result = $cache->getItem($key, $success); if (!$success) { $result = doExpensiveStuff(); $cache->setItem($key, $result); } Get multiple rows from db 1 2 3 4 5 // Instantiate the cache instance using a namespace for the same type of items $cache = ZendCacheStorageFactory::factory(array( ’adapter’ => array( ’name’ => ’filesystem’ // With a namespace we can indicate the same type of items 178 Chapter 36. ZendCacheStorageAdapter
  • 217. Zend Framework 2 Documentation, Release 2.2.5 // -> So we can simple use the db id as cache key ’options’ => array( ’namespace’ => ’dbtable’ ), 6 7 8 9 ), ’plugins’ => array( // Don’t throw exceptions on cache errors ’exception_handler’ => array( ’throw_exceptions’ => false ), // We store database rows on filesystem so we need to serialize them ’Serializer’ ) 10 11 12 13 14 15 16 17 18 19 )); 20 21 22 23 24 25 26 27 28 29 30 31 // Load two rows from cache if possible $ids = array(1, 2); $results = $cache->getItems($ids); if (count($results) < count($ids)) { // Load rows from db if loading from cache failed $missingIds = array_diff($ids, array_keys($results)); $missingResults = array(); $query = ’SELECT * FROM dbtable WHERE id IN (’ . implode(’,’, $missingIds) . ’)’; foreach ($pdo->query($query, PDO::FETCH_ASSOC) as $row) { $missingResults[ $row[’id’] ] = $row; } 32 // Update cache items of the loaded rows from db $cache->setItems($missingResults); 33 34 35 // merge results from cache and db $results = array_merge($results, $missingResults); 36 37 38 } 36.23. Examples 179
  • 218. Zend Framework 2 Documentation, Release 2.2.5 180 Chapter 36. ZendCacheStorageAdapter
  • 219. CHAPTER 37 ZendCacheStorageCapabilities 37.1 Overview Storage capabilities describes how a storage adapter works and which features it supports. To get capabilities of a storage adapter, you can use the method getCapabilities() of the storage adapter but only the storage adapter and its plugins have permissions to change them. Because capabilities are mutable, for example, by changing some options, you can subscribe to the “change” event to get notifications; see the examples for details. If you are writing your own plugin or adapter, you can also change capabilities because you have access to the marker object and can create your own marker to instantiate a new object of ZendCacheStorageCapabilities. 37.2 Available Methods __construct(ZendCacheStorageStorageInterface $storage, stdClass $marker, array $capabilities = array(), ZendCacheStorageCapabilities|null $baseCapabilities = null) Constructor getSupportedDatatypes() Get supported datatypes. Return type array setSupportedDatatypes(stdClass $marker, array $datatypes) Set supported datatypes. Return type ZendCacheStorageCapabilities getSupportedMetadata() Get supported metadata. Return type array setSupportedMetadata(stdClass $marker, string $metadata) Set supported metadata. Return type ZendCacheStorageCapabilities 181
  • 220. Zend Framework 2 Documentation, Release 2.2.5 getMinTtl() Get minimum supported time-to-live. (Returning 0 means items never expire) Return type integer setMinTtl(stdClass $marker, int $minTtl) Set minimum supported time-to-live. Return type ZendCacheStorageCapabilities getMaxTtl() Get maximum supported time-to-live. Return type integer setMaxTtl(stdClass $marker, int $maxTtl) Set maximum supported time-to-live. Return type ZendCacheStorageCapabilities getStaticTtl() Is the time-to-live handled static (on write), or dynamic (on read). Return type boolean setStaticTtl(stdClass $marker, boolean $flag) Set if the time-to-live is handled statically (on write) or dynamically (on read). Return type ZendCacheStorageCapabilities getTtlPrecision() Get time-to-live precision. Return type float setTtlPrecision(stdClass $marker, float $ttlPrecision) Set time-to-live precision. Return type ZendCacheStorageCapabilities getUseRequestTime() Get the “use request time” flag status. Return type boolean setUseRequestTime(stdClass $marker, boolean $flag) Set the “use request time” flag. Return type ZendCacheStorageCapabilities getExpiredRead() Get flag indicating if expired items are readable. Return type boolean setExpiredRead(stdClass $marker, boolean $flag) Set if expired items are readable. Return type ZendCacheStorageCapabilities getMaxKeyLength() Get maximum key length. Return type integer 182 Chapter 37. ZendCacheStorageCapabilities
  • 221. Zend Framework 2 Documentation, Release 2.2.5 setMaxKeyLength(stdClass $marker, int $maxKeyLength) Set maximum key length. Return type ZendCacheStorageCapabilities getNamespaceIsPrefix() Get if namespace support is implemented as a key prefix. Return type boolean setNamespaceIsPrefix(stdClass $marker, boolean $flag) Set if namespace support is implemented as a key prefix. Return type ZendCacheStorageCapabilities getNamespaceSeparator() Get namespace separator if namespace is implemented as a key prefix. Return type string setNamespaceSeparator(stdClass $marker, string $separator) Set the namespace separator if namespace is implemented as a key prefix. Return type ZendCacheStorageCapabilities 37.3 Examples Get storage capabilities and do specific stuff in base of it 1 use ZendCacheStorageFactory; 2 3 4 $cache = StorageFactory::adapterFactory(’filesystem’); $supportedDatatypes = $cache->getCapabilities()->getSupportedDatatypes(); 5 6 7 8 9 10 11 // now you can run specific stuff in base of supported feature if ($supportedDatatypes[’object’]) { $cache->set($key, $object); } else { $cache->set($key, serialize($object)); } Listen to change event 1 use ZendCacheStorageFactory; 2 3 4 5 $cache = StorageFactory::adapterFactory(’filesystem’, array( ’no_atime’ => false, )); 6 7 8 9 10 // Catching capability changes $cache->getEventManager()->attach(’capability’, function($event) { echo count($event->getParams()) . ’ capabilities changed’; }); 11 12 13 // change option which changes capabilities $cache->getOptions()->setNoATime(true); 37.3. Examples 183
  • 222. Zend Framework 2 Documentation, Release 2.2.5 184 Chapter 37. ZendCacheStorageCapabilities
  • 223. CHAPTER 38 ZendCacheStoragePlugin 38.1 Overview Cache storage plugins are objects to add missing functionality or to influence behavior of a storage adapter. The plugins listen to events the adapter triggers and can change called method arguments (*.post - events), skipping and directly return a result (using stopPropagation), changing the result (with setResult of ZendCacheStoragePostEvent) and catching exceptions (with ZendCacheStorageExceptionEvent). 38.2 Quick Start Storage plugins can either be created from ZendCacheStorageFactory with the pluginFactory, or by simply instantiating one of the ZendCacheStoragePlugin*classes. To make life easier, the ZendCacheStorageFactory comes with the method factory to create an adapter and all given plugins at once. 1 use ZendCacheStorageFactory; 2 3 4 5 6 7 // Via factory: $cache = StorageFactory::factory(array( ’adapter’ => ’filesystem’, ’plugins’ => array(’serializer’), )); 8 9 10 11 12 // Alternately: $cache = StorageFactory::adapterFactory(’filesystem’); $plugin = StorageFactory::pluginFactory(’serializer’); $cache->addPlugin($plugin); 13 14 15 16 17 // Or manually: $cache = new ZendCacheStorageAdapterFilesystem(); $plugin = new ZendCacheStoragePluginSerializer(); $cache->addPlugin($plugin); 185
  • 224. Zend Framework 2 Documentation, Release 2.2.5 38.3 The ClearExpiredByFactor Plugin The ZendCacheStorageAdapterClearExpiredByFactor plugin calls the storage method clearExpired() randomly (by factor) after every call of setItem(), setItems(), addItem() and addItems(). Table 38.1: Plugin specific options Name clearing_factor Data Type integer Default Value 0 Description The automatic clearing factor Note: ** The ClearExpiredInterface is required ** The storage have to implement the ZendCacheStorageClearExpiredInterface to work with this plugin. 38.4 The ExceptionHandler Plugin The ZendCacheStorageAdapterExceptionHandler plugin catches all exceptions thrown on reading or writing to cache and sends the exception to a defined callback function. It’s configurable if the plugin should re-throw the catched exception. Table 38.2: Plugin specific options Name Data Type Default Value null excepcallable tion_callback null throw_exceptions boolean Description Callback will be called on an exception and get the exception as argument Re-throw catched exceptions true 38.5 The IgnoreUserAbort Plugin The ZendCacheStorageAdapterIgnoreUserAbort plugin ignores script terminations by users until write operations to cache finished. Table 38.3: Plugin specific options Name exit_on_abort Data Type boolean Default Value true Description Terminate script execution if user abort the script 38.6 The OptimizeByFactor Plugin The ZendCacheStorageAdapterOptimizeByFactor plugin calls the storage method optimize() randomly (by factor) after removing items from cache. Table 38.4: Plugin specific options Name optimizing_factor 186 Data Type integer Default Value 0 Description The automatic optimization factor Chapter 38. ZendCacheStoragePlugin
  • 225. Zend Framework 2 Documentation, Release 2.2.5 Note: ** The OptimizableInterface is required ** The storage have to implement the ZendCacheStorageOptimizableInterface to work with this plugin. 38.7 The Serializer Plugin The ZendCacheStorageAdapterSerializer plugin will serialize data on writing to cache and unserialize on reading. So it’s possible to store different datatypes into cache storages only support strings. Table 38.5: Plugin specific options Name serializer Data Type Default Value Description null string null The serializer to use ZendSerializerAdapterAdapterInterface • If null use the default serializer • If string instantiate the serializer with serializer_options serializer_options array [] Array of serializer options used to instantiate the serializer 38.8 Available Methods setOptions(ZendCacheStoragePluginPluginOptions $options) Set options. Return type ZendCacheStoragePluginPluginInterface getOptions() Get options. Return type ZendCacheStoragePluginPluginOptions attach(ZendEventManagerEventManagerInterface $events) Defined by ZendEventManagerListenerAggregateInterface, attach one or more listeners. Return type void detach(ZendEventManagerEventManagerInterface $events) Defined by ZendEventManagerListenerAggregateInterface, detach all previously attached listeners. Return type void 38.7. The Serializer Plugin 187
  • 226. Zend Framework 2 Documentation, Release 2.2.5 38.9 Examples Basics of writing an own storage plugin 1 2 3 use ZendCacheStorageEvent; use ZendCacheStoragePluginAbstractPlugin; use ZendEventManagerEventManagerInterface; 4 5 6 class MyPlugin extends AbstractPlugin { 7 protected $handles = array(); 8 9 // This method have to attach all events required by this plugin public function attach(EventManagerInterface $events) { $this->handles[] = $events->attach(’getItem.pre’, array($this, ’onGetItemPre’)); $this->handles[] = $events->attach(’getItem.post’, array($this, ’onGetItemPost’)); return $this; } 10 11 12 13 14 15 16 17 // This method have to detach all events required by this plugin public function detach(EventManagerInterface $events) { foreach ($this->handles as $handle) { $events->detach($handle); } $this->handles = array(); return $this; } 18 19 20 21 22 23 24 25 26 27 public function onGetItemPre(Event $event) { $params = $event->getParams(); echo sprintf("Method ’getItem’ with key ’%s’ startedn", params[’key’]); } 28 29 30 31 32 33 public function onGetItemPost(Event $event) { $params = $event->getParams(); echo sprintf("Method ’getItem’ with key ’%s’ finishedn", params[’key’]); } 34 35 36 37 38 39 } 40 41 42 43 // After defining this basic plugin we can instantiate and add it to an adapter instance $plugin = new MyPlugin(); $cache->addPlugin($plugin); 44 45 46 47 48 // Now on calling getItem our basic plugin should print the expected output $cache->getItem(’cache-key’); // Method ’getItem’ with key ’cache-key’ started // Method ’getItem’ with key ’cache-key’ finished 188 Chapter 38. ZendCacheStoragePlugin
  • 227. CHAPTER 39 ZendCachePattern 39.1 Overview Cache patterns are configurable objects to solve known performance bottlenecks. Each should be used only in the specific situations they are designed to address. For example you can use one of the CallbackCache, ObjectCache or ClassCache patterns to cache method and function calls; to cache output generation, the OutputCache pattern could assist. All cache patterns implement the same interface, ZendCachePatternPatternInterface, and most extend the abstract class ZendCachePatternAbstractPattern to implement basic logic. Configuration is provided via the ZendCachePatternPatternOptions class, which can simply be instantiated with an associative array of options passed to the constructor. To configure a pattern object, you can set an instance of ZendCachePatternPatternOptions with setOptions, or provide your options (either as an associative array or PatternOptions instance) as the second argument to the factory. It’s also possible to use a single instance of ZendCachePatternPatternOptions and pass it to multiple pattern objects. 39.2 Quick Start Pattern objects can either be created from the provided ZendCachePatternFactory factory, or, by simply instantiating one of the ZendCachePattern*Cache classes. 1 2 3 4 1 2 3 4 5 // Via the factory: $callbackCache = ZendCachePatternFactory::factory(’callback’, array( ’storage’ => ’apc’, )); // OR, the equivalent manual instantiation: $callbackCache = new ZendCachePatternCallbackCache(); $callbackCache->setOptions(new ZendCachePatternPatternOptions(array( ’storage’ => ’apc’, ))); 189
  • 228. Zend Framework 2 Documentation, Release 2.2.5 39.3 Available Methods The following methods are implemented by ZendCachePatternAbstractPattern. Please read documentation of specific patterns to get more information. setOptions(ZendCachePatternPatternOptions $options) Set pattern options. Return type ZendCachePatternPatternInterface getOptions() Get all pattern options. Return type ZendCachePatternPatternOptions 190 Chapter 39. ZendCachePattern
  • 229. CHAPTER 40 ZendCachePatternCallbackCache 40.1 Overview The callback cache pattern caches calls of non specific functions and methods given as a callback. 40.2 Quick Start For instantiation you can use the PatternFactory or do it manual: 1 2 use ZendCachePatternFactory; use ZendCachePatternPatternOptions; 3 4 5 6 7 8 // Via the factory: $callbackCache = PatternFactory::factory(’callback’, array( ’storage’ => ’apc’, ’cache_output’ => true, )); 9 10 11 12 13 14 15 // OR, the equivalent manual instantiation: $callbackCache = new ZendCachePatternCallbackCache(); $callbackCache->setOptions(new PatternOptions(array( ’storage’ => ’apc’, ’cache_output’ => true, ))); 40.3 Configuration Options Option storage Data Type string array ZendCacheStorageStorageInterface cache_outputboolean Default Value <none> true Description The storage to write/read cached data Cache output of callback 191
  • 230. Zend Framework 2 Documentation, Release 2.2.5 40.4 Available Methods call(callable $callback, array $args = array()) Call the specified callback or get the result from cache. Return type mixed __call(string $function, array $args) Function call handler. Return type mixed generateKey(callable $callback, array $args = array()) Generate a unique key in base of a key representing the callback part and a key representing the arguments part. Return type string setOptions(ZendCachePatternPatternOptions $options) Set pattern options. Return type ZendCachePatternCallbackCache getOptions() Get all pattern options. Return type ZendCachePatternPatternOptions 40.5 Examples Instantiating the callback cache pattern 1 use ZendCachePatternFactory; 2 3 4 5 $callbackCache = PatternFactory::factory(’callback’, array( ’storage’ => ’apc’ )); 192 Chapter 40. ZendCachePatternCallbackCache
  • 231. CHAPTER 41 ZendCachePatternClassCache 41.1 Overview The ClassCache pattern is an extension to the CallbackCache pattern. It has the same methods but instead it generates the internally used callback in base of the configured class name and the given method name. 41.2 Quick Start Instantiating the class cache pattern 1 use ZendCachePatternFactory; 2 3 4 5 6 $classCache = PatternFactory::factory(’class’, array( ’class’ => ’MyClass’, ’storage’ => ’apc’ )); 41.3 Configuration Options Option Data Type Default Value storage string array <none> ZendCacheStorageStorageInterface class string <none> cache_output boolean true cache_by_default boolean true class_cache_methods array [] class_non_cache_methods array [] Description The storage to write/read cached data The class name Cache output of callback Cache method calls by default List of methods to cache (If cache_by_default is disabled) List of methods to no-cache (If cache_by_default is enabled) 193
  • 232. Zend Framework 2 Documentation, Release 2.2.5 41.4 Available Methods call(string $method, array $args = array()) Call the specified method of the configured class. Return type mixed __call(string $method, array $args) Call the specified method of the configured class. Return type mixed __set(string $name, mixed $value) Set a static property of the configured class. Return type void __get(string $name) Get a static property of the configured class. Return type mixed __isset(string $name) Checks if a static property of the configured class exists. Return type boolean __unset(string $name) Unset a static property of the configured class. Return type void generateKey(string $method, array $args = array()) Generate a unique key in base of a key representing the callback part and a key representing the arguments part. Return type string setOptions(ZendCachePatternPatternOptions $options) Set pattern options. Return type ZendCachePatternClassCache getOptions() Get all pattern options. Return type ZendCachePatternPatternOptions 41.5 Examples Caching of import feeds 194 Chapter 41. ZendCachePatternClassCache
  • 233. Zend Framework 2 Documentation, Release 2.2.5 1 2 3 $cachedFeedReader = ZendCachePatternFactory::factory(’class’, array( ’class’ => ’ZendFeedReaderReader’, ’storage’ => ’apc’, 4 // The feed reader doesn’t output anything // so the output don’t need to be caught and cached ’cache_output’ => false, 5 6 7 8 )); 9 10 11 12 $feed = $cachedFeedReader->call("import", array(’http://www.planet-php.net/rdf/’)); // OR $feed = $cachedFeedReader->import(’http://www.planet-php.net/rdf/’); 41.5. Examples 195
  • 234. Zend Framework 2 Documentation, Release 2.2.5 196 Chapter 41. ZendCachePatternClassCache
  • 235. CHAPTER 42 ZendCachePatternObjectCache 42.1 Overview The ObjectCache pattern is an extension to the CallbackCache pattern. It has the same methods but instead it generates the internally used callback in base of the configured object and the given method name. 42.2 Quick Start Instantiating the object cache pattern 1 use ZendCachePatternFactory; 2 3 4 5 6 7 $object = new stdClass(); $objectCache = PatternFactory::factory(’object’, array( ’object’ => $object, ’storage’ => ’apc’ )); 197
  • 236. Zend Framework 2 Documentation, Release 2.2.5 42.3 Configuration Options Option Data Type storage object object_key Default Value string array <none> ZendCacheStorageStorageInterface object <none> null string <Class name of object> boolean true boolean true array [] cache_output cache_by_default object_cache_methods obarray ject_non_cache_methods obboolean ject_cache_magic_properties [] false Description The storage to write/read cached data The object to cache methods calls of A hopefully unique key of the object Cache output of callback Cache method calls by default List of methods to cache (If cache_by_default is disabled) List of methods to no-cache (If cache_by_default is enabled) Cache calls of magic object properties 42.4 Available Methods call(string $method, array $args = array()) Call the specified method of the configured object. Return type mixed __call(string $method, array $args) Call the specified method of the configured object. Return type mixed __set(string $name, mixed $value) Set a property of the configured object. Return type void __get(string $name) Get a property of the configured object. Return type mixed __isset(string $name) Checks if static property of the configured object exists. Return type boolean __unset(string $name) Unset a property of the configured object. Return type void 198 Chapter 42. ZendCachePatternObjectCache
  • 237. Zend Framework 2 Documentation, Release 2.2.5 generateKey(string $method, array $args = array()) Generate a unique key in base of a key representing the callback part and a key representing the arguments part. Return type string setOptions(ZendCachePatternPatternOptions $options) Set pattern options. Return type ZendCachePatternObjectCache getOptions() Get all pattern options. Return type ZendCachePatternPatternOptions 42.5 Examples Caching a filter 1 2 3 4 5 $filter = new ZendFilterRealPath(); $cachedFilter = ZendCachePatternFactory::factory(’object’, array( ’object’ => $filter, ’object_key’ => ’RealpathFilter’, ’storage’ => ’apc’, 6 // The realpath filter doesn’t output anything // so the output don’t need to be caught and cached ’cache_output’ => false, 7 8 9 10 )); 11 12 13 14 $path = $cachedFilter->call("filter", array(’/www/var/path/../../mypath’)); // OR $path = $cachedFilter->filter(’/www/var/path/../../mypath’); 42.5. Examples 199
  • 238. Zend Framework 2 Documentation, Release 2.2.5 200 Chapter 42. ZendCachePatternObjectCache
  • 239. CHAPTER 43 ZendCachePatternOutputCache 43.1 Overview The OutputCache pattern caches output between calls to start() and end(). 43.2 Quick Start Instantiating the output cache pattern 1 use ZendCachePatternFactory; 2 3 4 5 $outputCache = PatternFactory::factory(’output’, array( ’storage’ => ’apc’ )); 43.3 Configuration Options Option storage Data Type string array ZendCacheStorageStorageInterface Default Value <none> Description The storage to write/read cached data 43.4 Available Methods start(string $key) If there is a cached item with the given key display it’s data and return true else start buffering output until end() is called or the script ends and return false. Return type boolean end() Stops buffering output, write buffered data to cache using the given key on start() and displays the buffer. 201
  • 240. Zend Framework 2 Documentation, Release 2.2.5 Return type boolean setOptions(ZendCachePatternPatternOptions $options) Set pattern options. Return type ZendCachePatternOutputCache getOptions() Get all pattern options. Return type ZendCachePatternPatternOptions 43.5 Examples Caching simple view scripts 1 2 3 $outputCache = ZendCachePatternFactory::factory(’output’, array( ’storage’ => ’apc’, )); 4 5 6 7 $outputCache->start(’mySimpleViewScript’); include ’/path/to/view/script.phtml’; $outputCache->end(); 202 Chapter 43. ZendCachePatternOutputCache
  • 241. CHAPTER 44 ZendCachePatternCaptureCache 44.1 Overview The CaptureCache pattern is useful to auto-generate static resources in base of a HTTP request. The Webserver needs to be configured to run a PHP script generating the requested resource so further requests for the same resource can be shipped without calling PHP again. It comes with basic logic to manage generated resources. 44.2 Quick Start Simplest usage as Apache-404 handler 1 2 1 2 3 4 5 # .htdocs ErrorDocument 404 /index.php // index.php use ZendCachePatternFactory; $capture = ZendCachePatternFactory::factory(’capture’, array( ’public_dir’ => __DIR__, )); 6 7 8 // Start capturing all output excl. headers and write to public directory $capture->start(); 9 10 11 // Don’t forget to change HTTP response code header(’Status: 200’, true, 200); 12 13 // do stuff to dynamically generate output 203
  • 242. Zend Framework 2 Documentation, Release 2.2.5 44.3 Configuration Options Option public_dir index_filename file_locking file_permission dir_permission umask Data Type string string boolean integer boolean integer boolean integer boolean Default Value <none> “index.html” true 0600 (false on win) 0700 (false on win) false Description Location of public directory to write output to The name of the first file if only a directory was requested Locking output files on writing Set permissions of generated output files Set permissions of generated output directories Using umask on generating output files / directories 44.4 Available Methods start(string|null $pageId = null) Start capturing output. Return type void set(string $content, string|null $pageId = null) Write content to page identity. Return type void get(string|null $pageId = null) Get content of an already cached page. Return type string|false has(string|null $pageId = null) Check if a page has been created. Return type boolean remove(string|null $pageId = null) Remove a page. Return type boolean clearByGlob(string $pattern = ‘**’) Clear pages matching glob pattern. Return type void setOptions(ZendCachePatternPatternOptions $options) Set pattern options. Return type ZendCachePatternCaptureCache getOptions() Get all pattern options. Return type ZendCachePatternPatternOptions 204 Chapter 44. ZendCachePatternCaptureCache
  • 243. Zend Framework 2 Documentation, Release 2.2.5 44.5 Examples Scaling images in base of request 1 2 1 2 3 4 # .htdocs ErrorDocument 404 /index.php // index.php $captureCache = ZendCachePatternFactory::factory(’capture’, array( ’public_dir’ => __DIR__, )); 5 6 // TODO 44.5. Examples 205
  • 244. Zend Framework 2 Documentation, Release 2.2.5 206 Chapter 44. ZendCachePatternCaptureCache
  • 245. CHAPTER 45 Introduction to ZendCaptcha CAPTCHA stands for “Completely Automated Public Turing test to tell Computers and Humans Apart”; it is used as a challenge-response to ensure that the individual submitting information is a human and not an automated process. Typically, a captcha is used with form submissions where authenticated users are not necessary, but you want to prevent spam submissions. Captchas can take a variety of forms, including asking logic questions, presenting skewed fonts, and presenting multiple images and asking how they relate. The ZendCaptcha component aims to provide a variety of back ends that may be utilized either standalone or in conjunction with the ZendForm component. 207
  • 246. Zend Framework 2 Documentation, Release 2.2.5 208 Chapter 45. Introduction to ZendCaptcha
  • 247. CHAPTER 46 Captcha Operation All CAPTCHA adapters implement ZendCaptchaAdapterInterface, which looks like the following: 1 namespace ZendCaptcha; 2 3 use ZendValidatorValidatorInterface; 4 5 6 7 interface AdapterInterface extends ValidatorInterface { public function generate(); 8 public function setName($name); 9 10 public function getName(); 11 12 // Get helper name used for rendering this captcha type public function getHelperName(); 13 14 15 } The name setter and getter are used to specify and retrieve the CAPTCHA identifier. The most interesting methods are generate() and render(). generate() is used to create the CAPTCHA token. This process typically will store the token in the session so that you may compare against it in subsequent requests. render() is used to render the information that represents the CAPTCHA, be it an image, a figlet, a logic problem, or some other CAPTCHA. A simple use case might look like the following: 1 2 3 4 5 6 // Originating request: $captcha = new ZendCaptchaFiglet(array( ’name’ => ’foo’, ’wordLen’ => 6, ’timeout’ => 300, )); 7 8 $id = $captcha->generate(); 9 10 11 //this will output a Figlet string echo $captcha->getFiglet()->render($captcha->getWord()); 12 13 14 15 // On a subsequent request: // Assume a captcha setup as before, with corresponding form fields, the value of $_POST[’foo’] 209
  • 248. Zend Framework 2 Documentation, Release 2.2.5 16 17 18 19 // would be key/value array: id => captcha ID, input => captcha value if ($captcha->isValid($_POST[’foo’], $_POST)) { // Validated! } Note: Under most circumstances, you probably prefer the use of ZendCaptcha functionality combined with the power of the ZendForm component. For an example on how to use ZendFormElementCaptcha, have a look at the ZendForm Quick Start. 210 Chapter 46. Captcha Operation
  • 249. CHAPTER 47 CAPTCHA Adapters The following adapters are shipped with Zend Framework by default. 47.1 ZendCaptchaAbstractWord ZendCaptchaAbstractWord is an abstract adapter that serves as the base class for most other CAPTCHA adapters. It provides mutators for specifying word length, session TTL and the session container object to use. ZendCaptchaAbstractWord also encapsulates validation logic. By default, the word length is 8 characters, the session timeout is 5 minutes, and ZendSessionContainer is used for persistence (using the namespace “ZendFormCaptcha<captcha ID>”). In addition to the methods required by the ZendCaptchaAdapterInterface ZendCaptchaAbstractWord exposes the following methods: interface, • setWordLen($length) and getWordLen() allow you to specify the length of the generated “word” in characters, and to retrieve the current value. • setTimeout($ttl) and getTimeout() allow you to specify the time-to-live of the session token, and to retrieve the current value. $ttl should be specified in seconds. • setUseNumbers($numbers) and getUseNumbers() allow you to specify if numbers will be considered as possible characters for the random work or only letters would be used. • setSessionClass($class) and getSessionClass() allow you to specify an alternate ZendSessionContainer implementation to use to persist the CAPTCHA token and to retrieve the current value. • getId() allows you to retrieve the current token identifier. • getWord() allows you to retrieve the generated word to use with the CAPTCHA. It will generate the word for you if none has been generated yet. • setSession(ZendSessionContainer $session) allows you to specify a session object to use for persisting the CAPTCHA token. getSession() allows you to retrieve the current session object. All word CAPTCHAs allow you to pass an array of options or Traversable object to the constructor, or, alternately, pass them to setOptions(). By default, the wordLen, timeout, and sessionClass keys may all be used. Each concrete implementation may define additional keys or utilize the options in other ways. 211
  • 250. Zend Framework 2 Documentation, Release 2.2.5 Note: ZendCaptchaAbstractWord is an abstract class and may not be instantiated directly. 47.2 ZendCaptchaDumb The ZendCaptchaDumb adapter is mostly self-descriptive. It provides a random string that must be typed in reverse to validate. As such, it’s not a good CAPTCHA solution and should only be used for testing. It extends ZendCaptchaAbstractWord. 47.3 ZendCaptchaFiglet The ZendCaptchaFiglet adapter utilizes ZendTextFiglet to present a figlet to the user. Options passed to the constructor will also be passed to the ZendTextFiglet object. See the ZendTextFiglet documentation for details on what configuration options are available. 47.4 ZendCaptchaImage The ZendCaptchaImage adapter takes the generated word and renders it as an image, performing various skewing permutations to make it difficult to automatically decipher. It requires the GD extension compiled with TrueType or Freetype support. Currently, the ZendCaptchaImage adapter can only generate PNG images. ZendCaptchaImage extends ZendCaptchaAbstractWord, and additionally exposes the following methods: • setExpiration($expiration) and getExpiration() allow you to specify a maximum lifetime the CAPTCHA image may reside on the filesystem. This is typically a longer than the session lifetime. Garbage collection is run periodically each time the CAPTCHA object is invoked, deleting all images that have expired. Expiration values should be specified in seconds. • setGcFreq($gcFreq) and getGcFreg() allow you to specify how frequently garbage collection should run. Garbage collection will run every 1/$gcFreq calls. The default is 100. • setFont($font) and getFont() allow you to specify the font you will use. $font should be a fully qualified path to the font file. This value is required; the CAPTCHA will throw an exception during generation if the font file has not been specified. • setFontSize($fsize) and getFontSize() allow you to specify the font size in pixels for generating the CAPTCHA. The default is 24px. • setHeight($height) and getHeight() allow you to specify the height in pixels of the generated CAPTCHA image. The default is 50px. • setWidth($width) and getWidth() allow you to specify the width in pixels of the generated CAPTCHA image. The default is 200px. • setImgDir($imgDir) and getImgDir() allow you to specify the directory for storing CAPTCHA images. The default is “./images/captcha/”, relative to the bootstrap script. • setImgUrl($imgUrl) and getImgUrl() allow you to specify the relative path to a CAPTCHA image to use for HTML markup. The default is “/images/captcha/”. • setSuffix($suffix) and getSuffix() allow you to specify the filename suffix for the CAPTCHA image. The default is “.png”. Note: changing this value will not change the type of the generated image. 212 Chapter 47. CAPTCHA Adapters
  • 251. Zend Framework 2 Documentation, Release 2.2.5 • setDotNoiseLevel($level) and getDotNoiseLevel(), along with setLineNoiseLevel($level) and getLineNoiseLevel(), allow you to control how much “noise” in the form of random dots and lines the image would contain. Each unit of $level produces one random dot or line. The default is 100 dots and 5 lines. The noise is added twice - before and after the image distortion transformation. All of the above options may be passed to the constructor by simply removing the ‘set’ method prefix and casting the initial letter to lowercase: “suffix”, “height”, “imgUrl”, etc. 47.5 ZendCaptchaReCaptcha The ZendCaptchaReCaptcha adapter uses ZendServiceReCaptchaReCaptcha to generate and validate CAPTCHAs. It exposes the following methods: • setPrivKey($key) and getPrivKey() allow you to specify the private key to use for the ReCaptcha service. This must be specified during construction, although it may be overridden at any point. • setPubKey($key) and getPubKey() allow you to specify the public key to use with the ReCaptcha service. This must be specified during construction, although it may be overridden at any point. • setService(ZendServiceReCaptchaReCaptcha $service) and getService() allow you to set and get the ReCaptcha service object. 47.5. ZendCaptchaReCaptcha 213
  • 252. Zend Framework 2 Documentation, Release 2.2.5 214 Chapter 47. CAPTCHA Adapters
  • 253. CHAPTER 48 Introduction to ZendConfig ZendConfig is designed to simplify access to configuration data within applications. It provides a nested object property-based user interface for accessing this configuration data within application code. The configuration data may come from a variety of media supporting hierarchical data storage. Currently, ZendConfig provides adapters that read and write configuration data stored in .ini, JSON, YAML and XML files. 48.1 Using ZendConfigConfig with a Reader Class Normally, it is expected that users would use one of the reader classes to read a configuration file, but if configuration data are available in a PHP array, one may simply pass the data to ZendConfigConfig‘s constructor in order to utilize a simple object-oriented interface: 1 2 3 4 5 6 7 8 9 10 11 12 13 // An array of configuration data is given $configArray = array( ’webhost’ => ’www.example.com’, ’database’ => array( ’adapter’ => ’pdo_mysql’, ’params’ => array( ’host’ => ’db.example.com’, ’username’ => ’dbuser’, ’password’ => ’secret’, ’dbname’ => ’mydatabase’ ) ) ); 14 15 16 // Create the object-oriented wrapper using the configuration data $config = new ZendConfigConfig($configArray); 17 18 19 // Print a configuration datum (results in ’www.example.com’) echo $config->webhost; As illustrated in the example above, ZendConfigConfig provides nested object property syntax to access configuration data passed to its constructor. Along with the object-oriented access to the data values, ZendConfigConfig also has get() method that returns the supplied value if the data element doesn’t exist in the configuration array. For example: 215
  • 254. Zend Framework 2 Documentation, Release 2.2.5 1 $host = $config->database->get(’host’, ’localhost’); 48.2 Using ZendConfigConfig with a PHP Configuration File It is often desirable to use a purely PHP-based configuration file. The following code illustrates how easily this can be accomplished: 1 2 3 4 5 6 7 8 9 10 11 12 13 1 2 // config.php return array( ’webhost’ => ’www.example.com’, ’database’ => array( ’adapter’ => ’pdo_mysql’, ’params’ => array( ’host’ => ’db.example.com’, ’username’ => ’dbuser’, ’password’ => ’secret’, ’dbname’ => ’mydatabase’ ) ) ); // Consumes the configuration array $config = new ZendConfigConfig(include ’config.php’); 3 4 5 // Print a configuration datum (results in ’www.example.com’) echo $config->webhost; 216 Chapter 48. Introduction to ZendConfig
  • 255. CHAPTER 49 Theory of Operation Configuration data are made accessible to ZendConfigConfig‘s constructor with an associative array, which may be multi-dimensional, so data can be organized from general to specific. Concrete adapter classes adapt configuration data from storage to produce the associative array for ZendConfigConfig‘s constructor. If needed, user scripts may provide such arrays directly to ZendConfigConfig‘s constructor, without using a reader class. Each value in the configuration data array becomes a property of the ZendConfigConfig object. The key is used as the property name. If a value is itself an array, then the resulting object property is created as a new ZendConfigConfig object, loaded with the array data. This occurs recursively, such that a hierarchy of configuration data may be created with any number of levels. ZendConfigConfig implements the Countable and Iterator interfaces in order to facilitate simple access to configuration data. Thus, ZendConfigConfig objects support the count() function and PHP constructs such as foreach. By default, configuration data made available through ZendConfigConfig are read-only, and an assignment (e.g. $config->database->host = ’example.com’;) results in a thrown exception. This default behavior may be overridden through the constructor, allowing modification of data values. Also, when modifications are allowed, ZendConfigConfig supports unsetting of values (i.e. unset($config->database->host)). The isReadOnly() method can be used to determine if modifications to a given ZendConfigConfig object are allowed and the setReadOnly() method can be used to stop any further modifications to a ZendConfigConfig object that was created allowing modifications. Note: Modifying Config does not save changes It is important not to confuse such in-memory modifications with saving configuration data out to specific storage media. Tools for creating and modifying configuration data for various storage media are out of scope with respect to ZendConfigConfig. Third-party open source solutions are readily available for the purpose of creating and modifying configuration data for various storage media. If you have two ZendConfigConfig objects, you can merge them into a single object using the merge() function. For example, given $config and $localConfig, you can merge data from $localConfig to $config using $config->merge($localConfig);. The items in $localConfig will override any items with the same name in $config. Note: The ZendConfigConfig object that is performing the merge must have been constructed to allow modifications, by passing TRUE as the second parameter of the constructor. The setReadOnly() method can then be used to prevent any further modifications after the merge is complete. 217
  • 256. Zend Framework 2 Documentation, Release 2.2.5 218 Chapter 49. Theory of Operation
  • 257. CHAPTER 50 ZendConfigReader ZendConfigReader gives you the ability to read a config file. It works with concrete implementations for different file format. The ZendConfigReader is only an interface, that define the two methods fromFile() and fromString(). The concrete implementations of this interface are: • ZendConfigReaderIni • ZendConfigReaderXml • ZendConfigReaderJson • ZendConfigReaderYaml The fromFile() and fromString() return a PHP array contains the data of the configuration file. Note: Differences from ZF1 The ZendConfigReader component no longer supports the following features: • Inheritance of sections. • Reading of specific sections. 50.1 ZendConfigReaderIni ZendConfigReaderIni enables developers to store configuration data in a familiar INI format and read them in the application by using an array syntax. ZendConfigReaderIni utilizes the parse_ini_file() PHP function. Please review this documentation to be aware of its specific behaviors, which propagate to ZendConfigReaderIni, such as how the special values of “TRUE”, “FALSE”, “yes”, “no”, and “NULL” are handled. Note: Key Separator By default, the key separator character is the period character (“.”). This can be changed, however, using the setNestSeparator() method. For example: 1 2 $reader = new ZendConfigReaderIni(); $reader->setNestSeparator(’-’); 219
  • 258. Zend Framework 2 Documentation, Release 2.2.5 The following example illustrates a basic use of ZendConfigReaderIni for loading configuration data from an INI file. In this example there are configuration data for both a production system and for a staging system. Suppose we have the following INI configuration file: 1 2 3 4 5 6 webhost database.adapter database.params.host database.params.username database.params.password database.params.dbname = = = = = = ’www.example.com’ ’pdo_mysql’ ’db.example.com’ ’dbuser’ ’secret’ ’dbproduction’ We can use the ZendConfigReaderIni to read this INI file: 1 2 $reader = new ZendConfigReaderIni(); $data = $reader->fromFile(’/path/to/config.ini’); 3 4 5 echo $data[’webhost’] // prints "www.example.com" echo $data[’database’][’params’][’dbname’]; // prints "dbproduction" The ZendConfigReaderIni supports a feature to include the content of a INI file in a specific section of another INI file. For instance, suppose we have an INI file with the database configuration: 1 2 3 4 5 database.adapter database.params.host database.params.username database.params.password database.params.dbname = = = = = ’pdo_mysql’ ’db.example.com’ ’dbuser’ ’secret’ ’dbproduction’ We can include this configuration in another INI file, for instance: 1 2 webhost = ’www.example.com’ @include = ’database.ini’ If we read this file using the component ZendConfigReaderIni we will obtain the same configuration data structure of the previous example. The @include = ’file-to-include.ini’ can be used also in a subelement of a value. For instance we can have an INI file like that: 1 2 3 4 5 adapter params.host params.username params.password params.dbname = = = = = ’pdo_mysql’ ’db.example.com’ ’dbuser’ ’secret’ ’dbproduction’ And assign the @include as subelement of the database value: 1 2 webhost = ’www.example.com’ database.@include = ’database.ini’ 50.2 ZendConfigReaderXml ZendConfigReaderXml enables developers to read configuration data in a familiar XML format and read them in the application by using an array syntax. The root element of the XML file or string is irrelevant and may be named arbitrarily. 220 Chapter 50. ZendConfigReader
  • 259. Zend Framework 2 Documentation, Release 2.2.5 The following example illustrates a basic use of ZendConfigReaderXml for loading configuration data from an XML file. Suppose we have the following XML configuration file: 1 2 3 4 5 6 7 8 9 10 11 12 13 <?xml version="1.0" encoding="utf-8"?>?> <config> <webhost>www.example.com</webhost> <database> <adapter value="pdo_mysql"/> <params> <host value="db.example.com"/> <username value="dbuser"/> <password value="secret"/> <dbname value="dbproduction"/> </params> </database> </config> We can use the ZendConfigReaderXml to read this XML file: 1 2 $reader = new ZendConfigReaderXml(); $data = $reader->fromFile(’/path/to/config.xml’); 3 4 5 echo $data[’webhost’] // prints "www.example.com" echo $data[’database’][’params’][’dbname’]; // prints "dbproduction" ZendConfigReaderXml utilizes the XMLReader PHP class. Please review this documentation to be aware of its specific behaviors, which propagate to ZendConfigReaderXml. Using ZendConfigReaderXml we can include the content of XML files in a specific XML element. This is provided using the standard function XInclude of XML. To use this function you have to add the namespace xmlns:xi="http://www.w3.org/2001/XInclude" to the XML file. Suppose we have an XML files that contains only the database configuration: 1 2 3 4 5 6 7 8 9 10 11 12 <?xml version="1.0" encoding="utf-8"?> <config> <database> <adapter>pdo_mysql</adapter> <params> <host>db.example.com</host> <username>dbuser</username> <password>secret</password> <dbname>dbproduction</dbname> </params> </database> </config> We can include this configuration in another XML file, for instance: 1 2 3 4 5 <?xml version="1.0" encoding="utf-8"?> <config xmlns:xi="http://www.w3.org/2001/XInclude"> <webhost>www.example.com</webhost> <xi:include href="database.xml"/> </config> The syntax to include an XML file in a specific element is <xi:include href="file-to-include.xml"/> 50.2. ZendConfigReaderXml 221
  • 260. Zend Framework 2 Documentation, Release 2.2.5 50.3 ZendConfigReaderJson ZendConfigReaderJson enables developers to read configuration data in a JSON format and read them in the application by using an array syntax. The following example illustrates a basic use of ZendConfigReaderJson for loading configuration data from a JSON file. Suppose we have the following JSON configuration file: 1 { "webhost" : "www.example.com", "database" : { "adapter" : "pdo_mysql", "params" : { "host" : "db.example.com", "username" : "dbuser", "password" : "secret", "dbname" : "dbproduction" } } 2 3 4 5 6 7 8 9 10 11 12 } We can use the ZendConfigReaderJson to read this JSON file: 1 2 $reader = new ZendConfigReaderJson(); $data = $reader->fromFile(’/path/to/config.json’); 3 4 5 echo $data[’webhost’] // prints "www.example.com" echo $data[’database’][’params’][’dbname’]; // prints "dbproduction" ZendConfigReaderJson utilizes the ZendJsonJson class. Using ZendConfigReaderJson we can include the content of a JSON file in a specific JSON section or element. This is provided using the special syntax @include. Suppose we have a JSON file that contains only the database configuration: 1 { "database" : { "adapter" : "pdo_mysql", "params" : { "host" : "db.example.com", "username" : "dbuser", "password" : "secret", "dbname" : "dbproduction" } } 2 3 4 5 6 7 8 9 10 11 } We can include this configuration in another JSON file, for instance: 1 { "webhost" : "www.example.com", "@include" : "database.json" 2 3 4 } 222 Chapter 50. ZendConfigReader
  • 261. Zend Framework 2 Documentation, Release 2.2.5 50.4 ZendConfigReaderYaml ZendConfigReaderYaml enables developers to read configuration data in a YAML format and read them in the application by using an array syntax. In order to use the YAML reader we need to pass a callback to an external PHP library or use the Yaml PECL extension. The following example illustrates a basic use of ZendConfigReaderYaml that use the Yaml PECL extension. Suppose we have the following YAML configuration file: 1 2 3 4 5 6 7 8 webhost: www.example.com database: adapter: pdo_mysql params: host: db.example.com username: dbuser password: secret dbname: dbproduction We can use the ZendConfigReaderYaml to read this YAML file: 1 2 $reader = new ZendConfigReaderYaml(); $data = $reader->fromFile(’/path/to/config.yaml’); 3 4 5 echo $data[’webhost’] // prints "www.example.com" echo $data[’database’][’params’][’dbname’]; // prints "dbproduction" If you want to use an external YAML reader you have to pass the callback function in the constructor of the class. For instance, if you want to use the Spyc library: 1 2 // include the Spyc library require_once (’path/to/spyc.php’); 3 4 5 $reader = new ZendConfigReaderYaml(array(’Spyc’,’YAMLLoadString’)); $data = $reader->fromFile(’/path/to/config.yaml’); 6 7 8 echo $data[’webhost’] // prints "www.example.com" echo $data[’database’][’params’][’dbname’]; // prints "dbproduction" You can also instantiate the ZendConfigReaderYaml without any parameter and specify the YAML reader in a second moment using the setYamlDecoder() method. Using ZendConfigReaderYaml we can include the content of a YAML file in a specific YAML section or element. This is provided using the special syntax @include. Suppose we have a YAML file that contains only the database configuration: 1 2 3 4 5 6 7 database: adapter: pdo_mysql params: host: db.example.com username: dbuser password: secret dbname: dbproduction We can include this configuration in another YAML file, for instance: webhost: www.example.com @include: database.yaml 50.4. ZendConfigReaderYaml 223
  • 262. Zend Framework 2 Documentation, Release 2.2.5 224 Chapter 50. ZendConfigReader
  • 263. CHAPTER 51 ZendConfigWriter ZendConfigWriter gives you the ability to write config files out of array, ZendConfigConfig and any Traversable object. The ZendConfigWriter is an interface that defines two methods: toFile() and toString(). We have five specific writers that implement this interface: • ZendConfigWriterIni • ZendConfigWriterXml • ZendConfigWriterPhpArray • ZendConfigWriterJson • ZendConfigWriterYaml 51.1 ZendConfigWriterIni The INI writer has two modes for rendering with regard to sections. By default the top-level configuration is always written into section names. By calling $writer->setRenderWithoutSectionsFlags(true); all options are written into the global namespace of the INI file and no sections are applied. As an addition ZendConfigWriterIni has an additional option parameter nestSeparator, which defines with which character the single nodes are separated. The default is a single dot, like it is accepted by ZendConfigReaderIni by default. When modifying or creating a ZendConfigConfig object, there are some things to know. To create or modify a value, you simply say set the parameter of the Config object via the parameter accessor (->). To create a section in the root or to create a branch, you just create a new array (“$config->branch = array();”). Using ZendConfigWriterIni This example illustrates the basic use of ZendConfigWriterIni to create a new config file: 1 2 3 // Create the config object $config = new ZendConfigConfig(array(), true); $config->production = array(); 4 5 6 $config->production->webhost = ’www.example.com’; $config->production->database = array(); 225
  • 264. Zend Framework 2 Documentation, Release 2.2.5 7 8 9 10 11 $config->production->database->params = array(); $config->production->database->params->host = ’localhost’; $config->production->database->params->username = ’production’; $config->production->database->params->password = ’secret’; $config->production->database->params->dbname = ’dbproduction’; 12 13 14 $writer = new ZendConfigWriterIni(); echo $writer->toString($config); The result of this code is an INI string contains the following values: 1 2 3 4 5 6 [production] webhost = "www.example.com" database.params.host = "localhost" database.params.username = "production" database.params.password = "secret" database.params.dbname = "dbproduction" You can use the method toFile() to store the INI data in a file. 51.2 ZendConfigWriterXml The ZendConfigWriterXml can be used to generate an XML string or file starting from a ZendConfigConfig object. Using ZendConfigWriterXml This example illustrates the basic use of ZendConfigWriterXml to create a new config file: 1 2 3 // Create the config object $config = new ZendConfigConfig(array(), true); $config->production = array(); 4 5 6 7 8 9 10 11 $config->production->webhost = ’www.example.com’; $config->production->database = array(); $config->production->database->params = array(); $config->production->database->params->host = ’localhost’; $config->production->database->params->username = ’production’; $config->production->database->params->password = ’secret’; $config->production->database->params->dbname = ’dbproduction’; 12 13 14 $writer = new ZendConfigWriterXml(); echo $writer->toString($config); The result of this code is an XML string contains the following data: 1 2 3 4 5 6 7 8 9 <?xml version="1.0" encoding="UTF-8"?> <zend-config> <production> <webhost>www.example.com</webhost> <database> <params> <host>localhost</host> <username>production</username> <password>secret</password> 226 Chapter 51. ZendConfigWriter
  • 265. Zend Framework 2 Documentation, Release 2.2.5 10 11 12 13 14 <dbname>dbproduction</dbname> </params> </database> </production> </zend-config> You can use the method toFile() to store the XML data in a file. 51.3 ZendConfigWriterPhpArray The ZendConfigWriterPhpArray can be used to generate a PHP code that returns an array representation of an ZendConfigConfig object. Using ZendConfigWriterPhpArray This example illustrates the basic use of ZendConfigWriterPhpArray to create a new config file: 1 2 3 // Create the config object $config = new ZendConfigConfig(array(), true); $config->production = array(); 4 5 6 7 8 9 10 11 $config->production->webhost = ’www.example.com’; $config->production->database = array(); $config->production->database->params = array(); $config->production->database->params->host = ’localhost’; $config->production->database->params->username = ’production’; $config->production->database->params->password = ’secret’; $config->production->database->params->dbname = ’dbproduction’; 12 13 14 $writer = new ZendConfigWriterPhpArray(); echo $writer->toString($config); The result of this code is a PHP script that returns an array as follow: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php return array ( ’production’ => array ( ’webhost’ => ’www.example.com’, ’database’ => array ( ’params’ => array ( ’host’ => ’localhost’, ’username’ => ’production’, ’password’ => ’secret’, ’dbname’ => ’dbproduction’, ), ), ), ); You can use the method toFile() to store the PHP script in a file. 51.3. ZendConfigWriterPhpArray 227
  • 266. Zend Framework 2 Documentation, Release 2.2.5 51.4 ZendConfigWriterJson The ZendConfigWriterJson can be used to generate a PHP code that returns the JSON representation of a ZendConfigConfig object. Using ZendConfigWriterJson This example illustrates the basic use of ZendConfigWriterJson to create a new config file: 1 2 3 // Create the config object $config = new ZendConfigConfig(array(), true); $config->production = array(); 4 5 6 7 8 9 10 11 $config->production->webhost = ’www.example.com’; $config->production->database = array(); $config->production->database->params = array(); $config->production->database->params->host = ’localhost’; $config->production->database->params->username = ’production’; $config->production->database->params->password = ’secret’; $config->production->database->params->dbname = ’dbproduction’; 12 13 14 $writer = new ZendConfigWriterJson(); echo $writer->toString($config); The result of this code is a JSON string contains the following values: 1 2 3 4 5 6 7 8 9 10 { "webhost" : "www.example.com", "database" : { "params" : { "host" : "localhost", "username" : "production", "password" : "secret", "dbname" : "dbproduction" } } } You can use the method toFile() to store the JSON data in a file. The ZendConfigWriterJson class uses the ZendJsonJson component to convert the data in a JSON format. 51.5 ZendConfigWriterYaml The ZendConfigWriterYaml can be used to generate a PHP code that returns the YAML representation of a ZendConfigConfig object. In order to use the YAML writer we need to pass a callback to an external PHP library or use the Yaml PECL extension. Using ZendConfigWriterYaml This example illustrates the basic use of ZendConfigWriterYaml to create a new config file using the Yaml PECL extension: 228 Chapter 51. ZendConfigWriter
  • 267. Zend Framework 2 Documentation, Release 2.2.5 1 2 3 // Create the config object $config = new ZendConfigConfig(array(), true); $config->production = array(); 4 5 6 7 8 9 10 11 $config->production->webhost = ’www.example.com’; $config->production->database = array(); $config->production->database->params = array(); $config->production->database->params->host = ’localhost’; $config->production->database->params->username = ’production’; $config->production->database->params->password = ’secret’; $config->production->database->params->dbname = ’dbproduction’; 12 13 14 $writer = new ZendConfigWriterYaml(); echo $writer->toString($config); The result of this code is a YAML string contains the following values: 1 2 3 4 5 6 7 webhost: www.example.com database: params: host: localhost username: production password: secret dbname: dbproduction You can use the method toFile() to store the YAML data in a file. If you want to use an external YAML writer library you have to pass the callback function in the constructor of the class. For instance, if you want to use the Spyc library: 1 2 // include the Spyc library require_once (’path/to/spyc.php’); 3 4 5 $writer = new ZendConfigWriterYaml(array(’Spyc’,’YAMLDump’)); echo $writer->toString($config); 51.5. ZendConfigWriterYaml 229
  • 268. Zend Framework 2 Documentation, Release 2.2.5 230 Chapter 51. ZendConfigWriter
  • 269. CHAPTER 52 ZendConfigProcessor ZendConfigProcessor gives you the ability to perform some operations on a ZendConfigConfig object. The ZendConfigProcessor is an interface that defines two methods: process() and processValue(). These operations are provided by the following concrete implementations: • ZendConfigProcessorConstant: manage PHP constant values; • ZendConfigProcessorFilter: filter the configuration data using ZendFilter; • ZendConfigProcessorQueue: manage a queue of operations to apply to configuration data; • ZendConfigProcessorToken: find and replace specific tokens; • ZendConfigProcessorTranslator: ZendI18nTranslator; translate configuration values in other languages using Below we reported some examples for each type of processor. 52.1 ZendConfigProcessorConstant Using ZendConfigProcessorConstant This example illustrates the basic use of ZendConfigProcessorConstant: 1 2 3 4 define (’TEST_CONST’, ’bar’); // set true to ZendConfigConfig to allow modifications $config = new ZendConfigConfig(array(’foo’ => ’TEST_CONST’), true); $processor = new ZendConfigProcessorConstant(); 5 6 7 8 echo $config->foo . ’,’; $processor->process($config); echo $config->foo; This example returns the output: TEST_CONST, bar.. 231
  • 270. Zend Framework 2 Documentation, Release 2.2.5 52.2 ZendConfigProcessorFilter Using ZendConfigProcessorFilter This example illustrates the basic use of ZendConfigProcessorFilter: 1 2 3 use ZendFilterStringToUpper; use ZendConfigProcessorFilter as FilterProcessor; use ZendConfigConfig; 4 5 6 $config = new Config(array (’foo’ => ’bar’), true); $upper = new StringToUpper(); 7 8 $upperProcessor = new FilterProcessor($upper); 9 10 11 12 echo $config->foo . ’,’; $upperProcessor->process($config); echo $config->foo; This example returns the output: bar,BAR. 52.3 ZendConfigProcessorQueue Using ZendConfigProcessorQueue This example illustrates the basic use of ZendConfigProcessorQueue: 1 2 3 4 5 use use use use use ZendFilterStringToLower; ZendFilterStringToUpper; ZendConfigProcessorFilter as FilterProcessor; ZendConfigProcessorQueue; ZendConfigConfig; 6 7 8 9 $config = new Config(array (’foo’ => ’bar’), true); $upper = new StringToUpper(); $lower = new StringToLower(); 10 11 12 $lowerProcessor = new FilterProcessor($lower); $upperProcessor = new FilterProcessor($upper); 13 14 15 16 17 $queue = new Queue(); $queue->insert($upperProcessor); $queue->insert($lowerProcessor); $queue->process($config); 18 19 echo $config->foo; This example returns the output: bar. The filters in the queue are applied with a FIFO logic (First In, First Out). 232 Chapter 52. ZendConfigProcessor
  • 271. Zend Framework 2 Documentation, Release 2.2.5 52.4 ZendConfigProcessorToken Using ZendConfigProcessorToken This example illustrates the basic use of ZendConfigProcessorToken: 1 2 3 // set the Config to true to allow modifications $config = new Config(array(’foo’ => ’Value is TOKEN’), true); $processor = new TokenProcessor(); 4 5 6 7 8 $processor->addToken(’TOKEN’, ’bar’); echo $config->foo . ’,’; $processor->process($config); echo $config->foo; This example returns the output: Value is TOKEN,Value is bar. 52.5 ZendConfigProcessorTranslator Using ZendConfigProcessorTranslator This example illustrates the basic use of ZendConfigProcessorTranslator: 1 2 3 use ZendConfigConfig; use ZendConfigProcessorTranslator as TranslatorProcessor; use ZendI18nTranslatorTranslator; 4 5 $config = new Config(array(’animal’ => ’dog’), true); 6 7 8 9 10 11 12 13 /* * The following mapping would exist for the translation * loader you provide to the translator instance * $italian = array( ’dog’ => ’cane’ * * ); */ 14 15 16 17 $translator = new Translator(); // ... configure the translator ... $processor = new TranslatorProcessor($translator); 18 19 20 21 echo "English: {$config->animal}, "; $processor->process($config); echo "Italian: {$config->animal}"; This example returns the output: English: 52.4. ZendConfigProcessorToken dog, Italian: cane. 233
  • 272. Zend Framework 2 Documentation, Release 2.2.5 234 Chapter 52. ZendConfigProcessor
  • 273. CHAPTER 53 The Factory The factory gives you the ability to load a configuration file to an array or to ZendConfigConfig object. The factory has two purposes • Loading configuration file(s) • Storing a configuration file Note: Storing the configuration will be done to one file. The factory is not aware of merging two or more configurations and will not store it into multiple files. If you want to store particular configuration sections to a different file you should separate it manually. 53.1 Loading configuration file The next example illustrates how to load a single configuration file 1 2 //Load a php file as array $config = ZendConfigFactory::fromFile(__DIR__.’/config/my.config.php’); 3 4 5 //Load a xml file as Config object $config = ZendConfigFactory::fromFile(__DIR__.’/config/my.config.xml’, true); For merging multiple configuration files 53.2 Storing configuration file Sometimes you want to store the configuration to a file. Also this is really easy to do 235
  • 274. Zend Framework 2 Documentation, Release 2.2.5 236 Chapter 53. The Factory
  • 275. CHAPTER 54 Introduction to ZendConsole Zend Framework 2 features built-in console support. When a ZendApplication is run from a console window (a shell window or Windows command prompt), it will recognize this fact and prepare ZendMvc components to handle the request. Console support is enabled by default, but to function properly it requires at least one console route and one action controller to handle the request. • Console routing allows you to invoke controllers and action depending on command line parameters provided by the user. • Module Manager integration allows ZF2 applications and modules to display help and usage information, in case the command line has not been understood (no route matched). • Console-aware action controllers will receive a console request containing all named parameters and flags. They are able to send output back to the console window. • Console adapters provide a level of abstraction for interacting with console on different operating systems. • Console prompts can be used to interact with the user by asking him questions and retrieving input. 54.1 Writing console routes A console route defines required and optional command line parameters. When a route matches, it behaves analogical to a standard, http route and can point to a MVC controller and an action. Let’s assume that we’d like our application to handle the following command line: > zf user resetpassword [email protected] When a user runs our application (zf) with these parameters, we’d like to call action resetpassword of ApplicationControllerIndexController. Note: We will use zf to depict the entry point for your application, it can be shell script in application bin folder or simply an alias for php public/index.php First we need to create a route definition: user resetpassword <userEmail> 237
  • 276. Zend Framework 2 Documentation, Release 2.2.5 This simple route definition expects exactly 3 arguments: a literal “user”, literal “resetpassword” followed by a parameter we’re calling “userEmail”. Let’s assume we also accept one optional parameter, that will turn on verbose operation: user resetpassword [--verbose|-v] <userEmail> Now our console route expects the same 3 parameters but will also recognise an optional --verbose flag, or its shorthand version: -v. Note: The order of flags is ignored by ZendConsole. Flags can appear before positional parameters, after them or anywhere in between. The order of multiple flags is also irrelevant. This applies both to route definitions and the order that flags are used on the command line. Let’s use the definition above and configure our console route. Console routes are automatically loaded from the following location inside config file: 1 2 3 4 5 6 array( ’router’ => array( ’routes’ => array( // HTTP routes are defined here ) ), 7 ’console’ => array( ’router’ => array( ’routes’ => array( // Console routes go here ) ) ), 8 9 10 11 12 13 14 15 ) Let’s create our console route and point it to ApplicationControllerIndexController::resetpasswordAction() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // we could define routes for ApplicationControllerIndexController in Application module config fil // which is usually located at modules/application/config/module.config.php array( ’console’ => array( ’router’ => array( ’routes’ => array( ’user-reset-password’ => array( ’options’ => array( ’route’ => ’user resetpassword [--verbose|-v] <userEmail>’, ’defaults’ => array( ’controller’ => ’ApplicationControllerIndex’, ’action’ => ’password’ ) ) ) ) ) ) ) See Also: To learn more about console routes and how to use them, please read this chapter: Console routes and routing 238 Chapter 54. Introduction to ZendConsole
  • 277. Zend Framework 2 Documentation, Release 2.2.5 54.2 Handling console requests When a user runs our application from command line and arguments match our console route, a controller class will be instantiated and an action method will be called, just like it is with http requests. We will now add resetpassword action to ApplicationControllerIndexController: 1 2 <?php namespace ApplicationController; 3 4 5 6 7 use use use use ZendMvcControllerAbstractActionController; ZendViewModelViewModel; ZendConsoleRequest as ConsoleRequest; ZendMathRand; 8 9 10 11 12 13 14 class IndexController extends AbstractActionController { public function indexAction() { return new ViewModel(); // display standard index page } 15 public function resetpasswordAction() { $request = $this->getRequest(); 16 17 18 19 // Make sure that we are running in a console and the user has not tricked our // application into running this action from a public web server. if (!$request instanceof ConsoleRequest){ throw new RuntimeException(’You can only use this action from a console!’); } 20 21 22 23 24 25 // Get user email from console and check if the user used --verbose or -v flag $userEmail = $request->getParam(’userEmail’); $verbose = $request->getParam(’verbose’); 26 27 28 29 // reset new password $newPassword = Rand::getString(16); 30 31 32 // Fetch the user and change his password, then email him ... // [...] 33 34 35 if (!$verbose) { return "Done! $userEmail has received an email with his new password.n"; }else{ return "Done! New password for user $userEmail is ’$newPassword’. It has also been emaile } 36 37 38 39 40 } 41 42 } We have created resetpasswordAction() than retrieves current request and checks if it’s really coming from the console (as a precaution). In this example we do not want our action to be invocable from a web page. Because we have not defined any http route pointing to it, it should never be possible. However in the future, we might define a wildcard route or a 3rd party module might erroneously route some requests to our action - that is why we want to make sure that the request is always coming from a Console environment. All console arguments supplied by the user are accessible via $request->getParam() method. Flags will be represented by a booleans, where true means a flag has been used and false otherwise. 54.2. Handling console requests 239
  • 278. Zend Framework 2 Documentation, Release 2.2.5 When our action has finished working it returns a simple string that will be shown to the user in console window. See Also: There are different ways you can interact with console from a controller. It has been covered in more detail in the following chapter: Console-aware action controllers 54.3 Adding console usage info It is a common practice for console application to display usage information when run for the first time (without any arguments). This is also handled by ZendConsole together with MVC. Usage info in ZF2 console applications is provided by loaded modules. In case no console route matches console arguments, ZendConsole will query all loaded modules and ask for their console usage info. Let’s modify our ApplicationModule to provide usage info: 1 <?php 2 3 namespace Application; 4 5 6 7 8 use use use use ZendModuleManagerFeatureAutoloaderProviderInterface; ZendModuleManagerFeatureConfigProviderInterface; ZendModuleManagerFeatureConsoleUsageProviderInterface; ZendConsoleAdapterAdapterInterface as Console; 9 10 11 12 13 14 15 16 17 18 class Module implements AutoloaderProviderInterface, ConfigProviderInterface, ConsoleUsageProviderInterface { public function getConfig() { // [...] } // <- our module implement this feature and provides console usag 19 public function getAutoloaderConfig() { // [...] } 20 21 22 23 24 public function getConsoleUsage(Console $console) { return array( // Describe available commands ’user resetpassword [--verbose|-v] EMAIL’ 25 26 27 28 29 => ’Reset password for a user’, 30 // Describe expected parameters array( ’EMAIL’, ’Email of the user for a password reset’ ), array( ’--verbose|-v’, ’(optional) turn on verbose mode’ ), 31 32 33 ); 34 } 35 36 } Each module that implements ConsoleUsageProviderInterface will be queried for console usage info. On route mismatch, all info from all modules will be concatenated, formatted to console width and shown to the user. 240 Chapter 54. Introduction to ZendConsole
  • 279. Zend Framework 2 Documentation, Release 2.2.5 Note: The order of usage info displayed in the console is the order modules load. If you want your application to display important usage info first, change the order your modules are loaded. See Also: Modules can also provide an application banner (title). To learn more about the format expected from getConsoleUsage() and about application banners, please read this chapter: Console-aware modules 54.3. Adding console usage info 241
  • 280. Zend Framework 2 Documentation, Release 2.2.5 242 Chapter 54. Introduction to ZendConsole
  • 281. CHAPTER 55 Console routes and routing Zend Framework 2 has native MVC integration with console, which means that command line arguments are read and used to determine the appropriate action controller and action method that will handle the request. Actions can perform any number of task prior to returning a result, that will be displayed to the user in his console window. There are several routes you can use with Console. All of them are defined in ZendMvcRouterConsole* classes. See Also: Routes are used to handle real commands, but they are not used to create help messages (usage information). When a zf2 application is run in console for the first time (without arguments) it can display usage information that is provided by modules. To learn more about providing usage information, please read this chapter: Console-aware modules. 55.1 Router configuration All Console Routes are automatically read from the following configuration location: 1 2 3 4 5 6 7 // This can sit inside of modules/Application/config/module.config.php or any other module’s config. array( ’router’ => array( ’routes’ => array( // HTTP routes are here ) ), 8 ’console’ => array( ’router’ => array( ’routes’ => array( // Console routes go here ) ) ), 9 10 11 12 13 14 15 16 ) Console Routes will only be processed when the application is run inside console (terminal) window. They have no effect in web (http) request and will be ignored. It is possible to define only HTTP routes (only web application) or only Console routes (which means we want a console-only application which will refuse to run in a browser). A single route can be described with the following array: 243
  • 282. Zend Framework 2 Documentation, Release 2.2.5 1 2 3 4 5 6 7 8 9 10 11 12 // inside config.console.router.routes: // [...] ’my-first-route’ => array( ’type’ => ’simple’, // <- simple route is created by default, we can skip that ’options’ => array( ’route’ => ’foo bar’, ’defaults’ => array( ’controller’ => ’ApplicationControllerIndex’, ’action’ => ’password’ ) ) ) We have created a simple console route with a name my-first-route. It expects two parameters: foo and bar. If user puts these in a console, ApplicationControllerIndexController::passwordAction() action will be invoked. See Also: You can read more about how ZF2 routing works in this chapter. 55.2 Basic route This is the default route type for console. It recognizes the following types of parameters: • Literal parameters (i.e. create object (external|internal)) • Literal flags (i.e. --verbose --direct [-d] [-a]) • Positional value parameters (i.e. create <modelName> [<destination>]) • Value flags (i.e. --name=NAME [--method=METHOD]) 55.2.1 Literal parameters These parameters are expected to appear on the command line exactly the way they are spelled in the route. For example: 1 2 3 4 5 6 7 8 9 ’show-users’ => array( ’options’ => array( ’route’ => ’show users’, ’defaults’ => array( ’controller’ => ’ApplicationControllerUsers’, ’action’ => ’show’ ) ) ) This route will only match for the following command line > zf show users It expects mandatory literal parameters show users. It will not match if there are any more params, or if one of the words is missing. The order of words is also enforced. We can also provide optional literal parameters, for example: 244 Chapter 55. Console routes and routing
  • 283. Zend Framework 2 Documentation, Release 2.2.5 1 2 3 4 5 6 7 8 9 ’show-users’ => array( ’options’ => array( ’route’ => ’show [all] users’, ’defaults’ => array( ’controller’ => ’ApplicationControllerUsers’, ’action’ => ’show’ ) ) ) Now this route will match for both of these commands: > zf show users > zf show all users We can also provide parameter alternative: 1 2 3 4 5 6 7 8 9 ’show-users’ => array( ’options’ => array( ’route’ => ’show [all|deleted|locked|admin] users’, ’defaults’ => array( ’controller’ => ’ApplicationControllerUsers’, ’action’ => ’show’ ) ) ) This route will match both without and with second parameter being one of the words, which enables us to capture commands such: > zf show users > zf show locked users > zf show admin users etc. Note: Whitespaces in route definition are ignored. If you separate your parameters with more spaces, or separate alternatives and pipe characters with spaces, it won’t matter for the parser. The above route definition is equivalent to: show [ all | deleted | locked | admin ] users 55.2.2 Literal flags Flags are a common concept for console tools. You can define any number of optional and mandatory flags. The order of flags is ignored. The can be defined in any order and the user can provide them in any other order. Let’s create a route with optional long flags 1 2 3 4 5 6 7 8 9 ’check-users’ => array( ’options’ => array( ’route’ => ’check users [--verbose] [--fast] [--thorough]’, ’defaults’ => array( ’controller’ => ’ApplicationControllerUsers’, ’action’ => ’check’ ) ) ) This route will match for commands like: 55.2. Basic route 245
  • 284. Zend Framework 2 Documentation, Release 2.2.5 > > > > zf zf zf zf check check check check users users --fast users --verbose --thorough users --thorough --fast We can also define one or more mandatory long flags and group them as an alternative: 1 2 3 4 5 6 7 8 9 ’check-users’ => array( ’options’ => array( ’route’ => ’check users (--suspicious|--expired) [--verbose] [--fast] [--thorough]’, ’defaults’ => array( ’controller’ => ’ApplicationControllerUsers’, ’action’ => ’check’ ) ) ) This route will only match if we provide either --suspicious or --expired flag, i.e.: > zf check users --expired > zf check users --expired --fast > zf check users --verbose --thorough --suspicious We can also use short flags in our routes and group them with long flags for convenience, for example: 1 2 3 4 5 6 7 8 9 ’check-users’ => array( ’options’ => array( ’route’ => ’check users [--verbose|-v] [--fast|-f] [--thorough|-t]’, ’defaults’ => array( ’controller’ => ’ApplicationControllerUsers’, ’action’ => ’check’ ) ) ) Now we can use short versions of our flags: > zf check users -f > zf check users -v --thorough > zf check users -t -f -v 55.2.3 Positional value parameters Value parameters capture any text-based input and come in two forms - positional and flags. Positional value parameters are expected to appear in an exact position on the command line. Let’s take a look at the following route definition: 1 2 3 4 5 6 7 8 9 ’delete-user’ => array( ’options’ => array( ’route’ => ’delete user <userEmail>’, ’defaults’ => array( ’controller’ => ’ApplicationControllerUsers’, ’action’ => ’delete’ ) ) ) This route will match for commands like: 246 Chapter 55. Console routes and routing
  • 285. Zend Framework 2 Documentation, Release 2.2.5 > zf delete user [email protected] > zf delete user [email protected] We can access the email value by calling $this->getRequest()->getParam(’userEmail’) inside of our controller action (you can read more about accessing values here) We can also define optional positional value parameters by adding square brackets: 1 2 3 4 5 6 7 8 9 ’delete-user’ => array( ’options’ => array( ’route’ => ’delete user [<userEmail>]’, ’defaults’ => array( ’controller’ => ’ApplicationControllerUsers’, ’action’ => ’delete’ ) ) ) In this case, userEmail parameter will not be required for the route to match. If it is not provided, userEmail parameter will not be set. We can define any number of positional value parameters, for example: 1 2 3 4 5 6 7 8 9 ’create-user’ => array( ’options’ => array( ’route’ => ’create user <firstName> <lastName> <email> <position>’, ’defaults’ => array( ’controller’ => ’ApplicationControllerUsers’, ’action’ => ’create’ ) ) ) This allows us to capture commands such as: > zf create user Johnny Bravo [email protected] Entertainer Note: Command line arguments on all systems must be properly escaped, otherwise they will not be passed to our application correctly. For example, to create a user with two names and a complex position description, we could write something like this: > zf create user "Johnan Tom" Bravo [email protected] "Head of the Entertainment Department" 55.2.4 Value flag parameters Positional value parameters are only matched if they appear in the exact order as described in the route. If we do not want to enforce the order of parameters, we can define value flags. Value flags can be defined and matched in any order. They can digest text-based values, for example: 1 2 3 4 5 6 7 ’find-user’ => array( ’options’ => array( ’route’ => ’find user [--id=] [--firstName=] [--lastName=] [--email=] [--position=] ’, ’defaults’ => array( ’controller’ => ’ApplicationControllerUsers’, ’action’ => ’find’ ) 55.2. Basic route 247
  • 286. Zend Framework 2 Documentation, Release 2.2.5 ) 8 9 ) This route will match for any of the following routes: > > > > > > > zf zf zf zf zf zf zf find find find find find find find user user user user user user user --id 29110 --id=29110 --firstName=Johny --lastName=Bravo --lastName Bravo --firstName Johny --position=Executive --firstName=Bob --position "Head of the Entertainment Department" Note: The order of flags is irrelevant for the parser. Note: The parser understands values that are provided after equal symbol (=) and separated by a space. Values without whitespaces can be provided after = symbol or after a space. Values with one more whitespaces however, must be properly quoted and written after a space. In previous example, all value flags are optional. It is also possible to define mandatory value flags: 1 2 3 4 5 6 7 8 9 ’rename-user’ => array( ’options’ => array( ’route’ => ’rename user --id= [--firstName=] [--lastName=]’, ’defaults’ => array( ’controller’ => ’ApplicationControllerUsers’, ’action’ => ’rename’ ) ) ) The --id parameter is required for this route to match. The following commands will work with this route: > zf rename user --id 123 > zf rename user --id 123 --firstName Jonathan > zf rename user --id=123 --lastName=Bravo 55.3 Catchall route This special route will catch all console requests, regardless of the parameters provided. 1 2 3 4 5 6 7 8 9 ’default-route’ => array( ’options’ => array( ’type’ => ’catchall’, ’defaults’ => array( ’controller’ => ’ApplicationControllerIndex’, ’action’ => ’consoledefault’ ) ) ) Note: This route type is rarely used. You could use it as a last console route, to display usage information. Before you do so, read about the preferred way of displaying console usage information. It is the recommended way and will guarantee proper inter-operation with other modules in your application. 248 Chapter 55. Console routes and routing
  • 287. Zend Framework 2 Documentation, Release 2.2.5 55.4 Console routes cheat-sheet Param type Literal params Literal Literal alternative Literal, optional Literal, optional alternative Flags Flag long Flag long, optional Example route definition Explanation foo foo foo foo “foo” followed by “bar” “foo” followed by “bar” or “baz” “foo”, optional “bar” “foo”, optional “bar” or “baz” bar (bar|baz) [bar] [bar|baz] foo --bar foo [--bar] Flag long, optional, alternative Flag short Flag short, optional Flag short, optional, alternative Flag long/short alternative Value parameters Value positional param Value positional param, optional Value Flag foo [--bar|--baz] Value Flag, optional foo [--bar=] Parameter groups Literal params group Literal optional params group Long flags group Long optional flags group Short flags group Short optional flags group foo -b foo [-b] foo [-b|-z] foo [--bar|-b] foo <bar> foo [<bar>] foo --bar= “foo” as first parameter, “–bar” flag before or after “foo” as first parameter, optional “–bar” flag before or after “foo” as first parameter, optional “–bar” or “–baz”, before or after “foo” as first parameter, “-b” flag before or after “foo” as first parameter, optional “-b” flag before or after “foo” as first parameter, optional “-b” or “-z”, before or after “foo” as first parameter, optional “–bar” or “-b” before or after “foo” followed by any text (stored as “bar” param) “foo”, optionally followed by any text (stored as “bar” param) “foo” as first parameter, “–bar” with a value, before or after “foo” as first parameter, optionally “–bar” with a value, before or after foo “foo” followed by “bar” or “baz” (stored as “myParam” (bar|baz):myParam param) foo “foo” followed by optional “bar” or “baz” (stored as [bar|baz]:myParam “myParam” param) foo “foo”, “bar” or “baz” flag before or after (stored as (--bar|--baz):myParam “myParam” param) foo “foo”, optional “bar” or “baz” flag before or after (as [--bar|--baz]:myParam “myParam” param) foo “foo”, “-b” or “-z” flag before or after (stored as (-b|-z):myParam “myParam” param) foo “foo”, optional “-b” or “-z” flag before or after (stored [-b|-z]:myParam as “myParam” param) 55.4. Console routes cheat-sheet 249
  • 288. Zend Framework 2 Documentation, Release 2.2.5 250 Chapter 55. Console routes and routing
  • 289. CHAPTER 56 Console-aware modules Zend Framework 2 has native MVC integration with console. The integration also works with modules loaded with Module Manager. ZF2 ships with RouteNotFoundStrategy which is responsible of displaying usage information inside Console, in case the user has not provided any arguments, or arguments could not be understood. The strategy currently supports two types of information: application banners and usage information. 56.1 Application banner To run the console ZF 2 component, go to your public folder, and type php index.php. By default, it will simply output the current ZF 2 version, like this: Our Application module (and any other module) can provide application banner. In order to do so, our Module class has to implement ZendModuleManagerFeatureConsoleBannerProviderInterface. Let’s do this now. 251
  • 290. Zend Framework 2 Documentation, Release 2.2.5 1 2 3 // modules/Application/Module.php <?php namespace Application; 4 5 6 use ZendModuleManagerFeatureConsoleBannerProviderInterface; use ZendConsoleAdapterAdapterInterface as Console; 7 8 9 10 11 12 13 14 15 16 17 class Module implements ConsoleBannerProviderInterface { /** * This method is defined in ConsoleBannerProviderInterface */ public function getConsoleBanner(Console $console) { return ’MyModule 0.0.1’; } } As you can see, the application banner should be a single line string that returns the module’s name and (if available) its current version. If several modules define their own banner, they are all shown one after the other (they will be joined together in the order modules are loaded). This way, it makes it very easy to spot which modules provide console commands. After running our application, we’ll see our newly created banner. Let’s create and load second module that provides a banner. 1 2 3 4 5 6 7 <?php // config/application.config.php return array( ’modules’ => array( ’Application’, ’User’, // < load user module in modules/User ), User module will add-on a short info about itself: 252 Chapter 56. Console-aware modules
  • 291. Zend Framework 2 Documentation, Release 2.2.5 1 2 3 // modules/User/Module.php <?php namespace User; 4 5 6 use ZendModuleManagerFeatureConsoleBannerProviderInterface; use ZendConsoleAdapterAdapterInterface as Console; 7 8 9 10 11 12 13 14 15 16 class Module implements ConsoleBannerProviderInterface { /** * This method is defined in ConsoleBannerProviderInterface */ public function getConsoleBanner(Console $console){ return "User Module 0.0.1"; } } Because User module is loaded after Application module, the result will look like this: Note: Application banner is displayed as-is - no trimming or other adjustments will be performed on the text. As you can see, banners are also automatically colorized as blue. 56.2 Usage information In order to display usage information, our Module class has to implement ZendModuleManagerFeatureConsoleUsageProviderInterface. Let’s modify our example and add new method: 1 2 3 // modules/Application/Module.php <?php namespace Application; 4 5 use ZendModuleManagerFeatureConsoleBannerProviderInterface; 56.2. Usage information 253
  • 292. Zend Framework 2 Documentation, Release 2.2.5 6 7 use ZendModuleManagerFeatureConsoleUsageProviderInterface; use ZendConsoleAdapterAdapterInterface as Console; 8 9 10 11 class Module implements ConsoleBannerProviderInterface, ConsoleUsageProviderInterface { public function getConsoleBanner(Console $console){ // ... } 12 /** * This method is defined in ConsoleUsageProviderInterface */ public function getConsoleUsage(Console $console) { return array( ’show stats’ => ’Show application statistics’, ’run cron’ => ’Run automated jobs’, ’(enable|disable) debug’ => ’Enable or disable debug mode for the application.’ ); } 13 14 15 16 17 18 19 20 21 22 23 24 } This will display the following information: Similar to application banner multiple modules can provide usage information, which will be joined together and displayed to the user. The order in which usage information is displayed is the order in which modules are loaded. As you can see, Console component also prepended each module’s usage by the module’s name. This helps to visually 254 Chapter 56. Console-aware modules
  • 293. Zend Framework 2 Documentation, Release 2.2.5 separate each modules (this can be useful when you have multiple modules that provide commands). By default, the component colorizes those in red. Note: Usage info provided in modules does not connect with console routing. You can describe console usage in any form you prefer and it does not affect how MVC handles console commands. In order to handle real console requests you need to define 1 or more console routes. 56.2.1 Free-form text In order to output free-form text as usage information, getConsoleUsage() can return a string, or an array of strings, for example: 1 2 3 4 public function getConsoleUsage(Console $console) { return ’User module expects exactly one argument - user name. It will display information for thi } Note: The text provided is displayed as-is - no trimming or other adjustments will be performed. If you’d like to fit your usage information inside console window, you could check its width with $console->getWidth(). 56.2. Usage information 255
  • 294. Zend Framework 2 Documentation, Release 2.2.5 56.2.2 List of commands If getConsoleUsage() returns and associative array, it will be automatically aligned in 2 columns. The first column will be prepended with script name (the entry point for the application). This is useful to display different ways of running the application. 1 2 3 4 5 6 7 8 9 public function getConsoleUsage(Console $console) { return array( ’delete user <userEmail>’ => ’Delete user with email <userEmail>’, ’disable user <userEmail>’ => ’Disable user with email <userEmail>’, ’list [all|disabled] users’ => ’Show a list of users’, ’find user [--email=] [--name=]’ => ’Attempt to find a user by email or name’, ); } Note: Commands and their descriptions will be aligned in two columns, that fit inside Console window. If the window is resized, some texts might be wrapped but all content will be aligned accordingly. If you don’t like this behavior, you can always return free-form text that will not be transformed in any way. 56.2.3 List of params and flags Returning an array of arrays from getConsoleUsage() will produce a listing of parameters. This is useful for explaining flags, switches, possible values and other information. The output will be aligned in multiple columns for readability. Below is an example: 256 Chapter 56. Console-aware modules
  • 295. Zend Framework 2 Documentation, Release 2.2.5 1 2 3 4 5 6 7 8 9 10 public function getConsoleUsage(Console $console) { return array( array( ’<userEmail>’ , ’email of the user’ ), array( ’--verbose’ , ’Turn on verbose mode’ ), array( ’--quick’ , ’Perform a "quick" operation’ ), array( ’-v’ , ’Same as --verbose’ ), array( ’-w’ , ’Wide output’) ); } Using this method, we can display more than 2 columns of information, for example: 1 2 3 4 5 6 7 8 9 10 public function getConsoleUsage(Console $console) { return array( array( ’<userEmail>’ , ’user email’ array( ’--verbose’ , ’verbose mode’ array( ’--quick’ , ’"quick" operation’ array( ’-v’ , ’Same as --verbose’ array( ’-w’ , ’wide output’ ); } 56.2. Usage information , , , , , ’Full email address of the user to find.’ ), ’Display additional information during processin ’Do not check integrity, just make changes and f ’Display additional information during processin ’When listing users, use the whole available scr 257
  • 296. Zend Framework 2 Documentation, Release 2.2.5 Note: All info will be aligned in one or more columns that fit inside Console window. If the window is resized, some texts might be wrapped but all content will be aligned accordingly. In case the number of columns changes (i.e. the array() contains different number of elements) a new table will be started, with new alignment and different column widths. If you don’t like this behavior, you can always return free-form text that will not be transformed in any way. 56.2.4 Mixing styles You can use mix together all of the above styles to provide comprehensive usage information, for example: 1 2 3 4 5 6 public function getConsoleUsage(Console $console) { return array( ’Finding and listing users’, ’list [all|disabled] users [-w]’ => ’Show a list of users’, ’find user [--email=] [--name=]’ => ’Attempt to find a user by email or name’, 7 array(’[all|disabled]’, array(’--email=EMAIL’, array(’--name=NAME’, array(’-w’, 8 9 10 11 ’Display all users or only disabled accounts’), ’Email of the user to find’), ’Full name of the user to find.’), ’Wide output - When listing users use the whole available screen w 12 ’Manipulation of user database:’, ’delete user <userEmail> [--verbose|-v] [--quick]’ ’disable user <userEmail> [--verbose|-v]’ 13 14 15 => ’Delete user with email <userEmail>’, => ’Disable user with email <userEmail>’, 16 array( ’<userEmail>’ , ’user email’ array( ’--verbose’ , ’verbose mode’ 17 18 258 , ’Full email address of the user to change.’ ), , ’Display additional information during processin Chapter 56. Console-aware modules
  • 297. Zend Framework 2 Documentation, Release 2.2.5 array( ’--quick’ array( ’-v’ 19 20 , ’"quick" operation’ , ’Do not check integrity, just make changes and f , ’Same as --verbose’ , ’Display additional information during processin 21 ); 22 23 } 56.3 Best practices As a reminder, here are the best practices when providing usage for your commands: 1. Your getConsoleBanner should only return a one-line string containing the module’s name and its version (if available). 2. Your getConsoleUsage should not return module’s name; it is prepended automatically for you by Console component. 56.3. Best practices 259
  • 298. Zend Framework 2 Documentation, Release 2.2.5 260 Chapter 56. Console-aware modules
  • 299. CHAPTER 57 Console-aware action controllers Zend Framework 2 has built-in MVC integration with the console. When the user runs an application in a console window, the request will be routed. By matching command line arguments against console routes we have defined in our application, the MVC will invoke a controller and an action. In this chapter we will learn how ZF2 Controllers can interact with and return output to console window. See Also: In order for a controller to be invoked, at least one route must point to it. To learn about creating console routes, please read the chapter Console routes and routing 57.1 Handling console requests Console requests are very similar to HTTP requests. In fact, they implement a common interface and are created at the same time in the MVC workflow. Console routes match against command line arguments and provide a defaults array, which holds the controller and action keys. These correspond with controller aliases in the ServiceManager, and method names in the controller class. This is analogous to the way HTTP requests are handled in ZF2. See Also: To learn about defining and creating controllers, please read the chapter Routing and controllers In this example we’ll use the following simple route: 1 2 3 4 5 6 7 // FILE: modules/Application/config/module.config.php array( ’router’ => array( ’routes’ => array( // HTTP routes are here ) ), 8 9 10 11 12 13 14 ’console’ => array( ’router’ => array( ’routes’ => array( ’list-users’ => array( ’options’ => array( ’route’ => ’show [all|disabled|deleted]:mode users [--verbose|-v]’, 261
  • 300. Zend Framework 2 Documentation, Release 2.2.5 ’defaults’ => array( ’controller’ => ’ApplicationControllerIndex’, ’action’ => ’show-users’ ) 15 16 17 18 ) 19 ) 20 ) 21 ) 22 ), 23 24 ) This route will match commands such as: > php public/index.php show users > php public/index.php show all users > php public/index.php show disabled users This route points to the method ApplicationControllerIndexController::showUsersAction(). Let’s add it to our controller. 1 2 <?php namespace ApplicationController; 3 4 5 use ZendMvcControllerAbstractActionController; use ZendViewModelViewModel; 6 7 8 9 10 11 12 class IndexController extends AbstractActionController { public function indexAction() { return new ViewModel(); // display standard index page } 13 public function showUsersAction() { $request = $this->getRequest(); 14 15 16 17 // Check verbose flag $verbose = $request->getParam(’verbose’) || $request->getParam(’v’); 18 19 20 // Check mode $mode = $request->getParam(’mode’, ’all’); // defaults to ’all’ 21 22 23 $users = array(); switch ($mode) { case ’disabled’: $users = $this->getServiceLocator()->get(’users’)->fetchDisabledUsers(); break; case ’deleted’: $users = $this->getServiceLocator()->get(’users’)->fetchDeletedUsers(); break; case ’all’: default: $users = $this->getServiceLocator()->get(’users’)->fetchAllUsers(); break; } 24 25 26 27 28 29 30 31 32 33 34 35 36 } 37 38 } 262 Chapter 57. Console-aware action controllers
  • 301. Zend Framework 2 Documentation, Release 2.2.5 We fetch the console request, read parameters, and load users from our (theoretical) users service. In order to make this method functional, we’ll have to display the result in the console window. 57.2 Sending output to console The simplest way for our controller to display data in the console window is to return a string. Let’s modify our example to output a list of users: 1 2 3 public function showUsersAction() { $request = $this->getRequest(); 4 // Check verbose flag $verbose = $request->getParam(’verbose’) || $request->getParam(’v’); 5 6 7 // Check mode $mode = $request->getParam(’mode’, ’all’); // defaults to ’all’ 8 9 10 $users = array(); switch ($mode) { case ’disabled’: $users = $this->getServiceLocator()->get(’users’)->fetchDisabledUsers(); break; case ’deleted’: $users = $this->getServiceLocator()->get(’users’)->fetchDeletedUsers(); break; case ’all’: default: $users = $this->getServiceLocator()->get(’users’)->fetchAllUsers(); break; } 11 12 13 14 15 16 17 18 19 20 21 22 23 24 if (count($users) == 0) { // Show an error message in the console return "There are no users in the databasen"; } 25 26 27 28 29 $result = ’’; 30 31 foreach ($users as $user) { $result .= $user->name . ’ ’ . $user->email . "n"; } 32 33 34 35 return $result; // show it in the console 36 37 } On line 27, we are checking if the users service found any users - otherwise we are returning an error message that will be immediately displayed and the application will end. If there are 1 or more users, we will loop through them with and prepare a listing. It is then returned from the action and displayed in the console window. 57.2. Sending output to console 263
  • 302. Zend Framework 2 Documentation, Release 2.2.5 57.3 Are we in a console? Sometimes we might need to check if our method is being called from a console or from a web request. This is useful to block certain methods from running in the console or to change their behavior based on that context. Here is an example of how to check if we are dealing with a console request: 1 namespace ApplicationController; 2 3 4 5 6 use use use use ZendMvcControllerAbstractActionController; ZendViewModelViewModel; ZendConsoleRequest as ConsoleRequest; RuntimeException; 7 8 9 10 11 12 class IndexController extends AbstractActionController { public function showUsersAction() { $request = $this->getRequest(); 13 // Make sure that we are running in a console and the user has not tricked our // application into running this action from a public web server. if (!$request instanceof ConsoleRequest) { throw new RuntimeException(’You can only use this action from a console!’); } // ... 14 15 16 17 18 19 } 20 21 } Note: You do not need to secure all your controllers and methods from console requests. Controller actions will only be invoked when at least one console route matches it. HTTP and Console routes are separated and defined in different places in module (and application) configuration. There is no way to invoke a console action unless there is at least one route pointing to it. Similarly, there is no way for an HTTP action to be invoked unless there is at least one HTTP route that points to it. The example below shows how a single controller method can handle both Console and HTTP requests: 1 namespace ApplicationController; 2 3 4 5 6 7 use use use use use ZendMvcControllerAbstractActionController; ZendViewModelViewModel; ZendConsoleRequest as ConsoleRequest; ZendHttpRequest as HttpRequest; RuntimeException; 8 9 10 11 12 13 class IndexController extends AbstractActionController { public function showUsersAction() { $request = $this->getRequest(); 14 $users = array(); // ... fetch users from database ... 15 16 17 if ($request instanceof HttpRequest) { // display a web page with users list 18 19 264 Chapter 57. Console-aware action controllers
  • 303. Zend Framework 2 Documentation, Release 2.2.5 return new ViewModel($result); } elseif ($request instanceof ConsoleRequest) { // ... prepare console output and return it ... return $result; } else { throw new RuntimeException(’Cannot handle request of type ’ . get_class($request)); } 20 21 22 23 24 25 26 } 27 28 } 57.4 Reading values from console parameters There are several types of parameters recognized by the Console component - all of them are described in the console routing chapter. Here, we’ll focus on how to retrieve values from distinct parameters and flags. 57.4.1 Positional parameters After a route matches, we can access both literal parameters and value parameters from within the $request container. Assuming we have the following route: 1 2 3 4 5 6 7 8 9 10 // inside of config.console.router.routes: ’show-users’ => array( ’options’ => array( ’route’ => ’show (all|deleted|locked|admin) [<groupName>]’ ’defaults’ => array( ’controller’ => ’ApplicationControllerUsers’, ’action’ => ’showusers’ ) ) ) If this route matches, our action can now query parameters in the following way: 1 2 3 4 // an action inside ApplicationControllerUsersController: public function showUsersAction() { $request = $this->getRequest(); 5 // We can access named value parameters directly by their name: $showUsersFromGroup = $request->getParam(’groupName’); 6 7 8 // Literal parameters can be checked with isset() against their exact spelling if (isset($request->getParam(’all’))) { // show all users } elseif (isset($request->getParam(’deleted’))) { // show deleted users } // ... 9 10 11 12 13 14 15 16 } In case of parameter alternatives, it is a good idea to assign a name to the group, which simplifies the branching in our action controllers. We can do this with the following syntax: 57.4. Reading values from console parameters 265
  • 304. Zend Framework 2 Documentation, Release 2.2.5 1 2 3 4 5 6 7 8 9 10 // inside of config.console.router.routes: ’show-users’ => array( ’options’ => array( ’route’ => ’show (all|deleted|locked|admin):userTypeFilter [<groupName>]’ ’defaults’ => array( ’controller’ => ’ApplicationControllerUsers’, ’action’ => ’showusers’ ) ) ) Now we can use a the group name userTypeFilter to check which option has been selected by the user: 1 2 3 public function showUsersAction() { $request = $this->getRequest(); 4 // We can access named value parameters directly by their name: $showUsersFromGroup = $request->getParam(’groupName’); 5 6 7 // The selected option from second parameter is now stored under ’userTypeFilter’ $userTypeFilter = $request->getParam(’userTypeFilter’); 8 9 10 switch ($userTypeFilter) { case ’all’: // all users case ’deleted’: // deleted users case ’locked’ // ... // ... } 11 12 13 14 15 16 17 18 19 20 } 57.4.2 Flags Flags are directly accessible by name. Value-capturing flags will contain string values, as provided by the user. Nonvalue flags will be equal to true. Given the following route: 1 2 3 4 5 6 7 8 9 ’find-user’ => array( ’options’ => array( ’route’ => ’find user [--fast] [--verbose] [--id=] [--firstName=] [--lastName=] [--email=] ’defaults’ => array( ’controller’ => ’ApplicationControllerUsers’, ’action’ => ’find’, ) ) ) We can easily retrieve values in the following fashion: 1 2 3 public function findAction() { $request = $this->getRequest(); 4 // We can retrieve values from value flags using their name 5 266 Chapter 57. Console-aware action controllers
  • 305. Zend Framework 2 Documentation, Release 2.2.5 $searchId $searchFirstName $searchLastName $searchEmail 6 7 8 9 = = = = $request->getParam(’id’, $request->getParam(’firstName’, $request->getParam(’lastName’, $request->getParam(’email’, null); // default null null); null); null); 10 // Standard flags that have been matched will be equal to TRUE $isFast = (bool) $request->getParam(’fast’, false); // default false $isVerbose = (bool) $request->getParam(’verbose’,false); 11 12 13 14 if ($isFast) { // perform a fast query ... } else { // perform standard query ... } 15 16 17 18 19 20 } In case of flag alternatives, we have to check each alternative separately: 1 2 3 4 5 6 // Assuming our route now reads: // ’route’ => ’find user [--fast|-f] [--verbose|-v] ... ’, // public function findAction() { $request = $this->getRequest(); 7 // Check both alternatives $isFast = $request->getParam(’fast’,false) || $request->getParam(’f’,false); $isVerbose = $request->getParam(’verbose’,false) || $request->getParam(’v’,false); 8 9 10 11 // ... 12 13 } 57.4. Reading values from console parameters 267
  • 306. Zend Framework 2 Documentation, Release 2.2.5 268 Chapter 57. Console-aware action controllers
  • 307. CHAPTER 58 Console adapters Zend Framework 2 provides console abstraction layer, which works around various bugs and limitations in operating systems. It handles displaying of colored text, retrieving console window size, charset and provides basic line drawing capabilities. See Also: Console Adapters can be used for a low-level access to the console. If you plan on building functional console applications you do not normally need to use adapters. Make sure to read about console MVC integration first, because it provides a convenient way for running modular console applications without directly writing to or reading from console window. 58.1 Retrieving console adapter If you are using MVC controllers you can obtain Console adapter instance using Service Manager. 1 namespace Application; 2 3 4 5 use ZendMvcControllerAbstractActionController; use ZendConsoleAdapterAdapterInterface as Console; use ZendConsoleExceptionRuntimeException; 6 7 8 9 10 11 12 13 14 15 16 class ConsoleController extends AbstractActionController { public function testAction() { $console = $this->getServiceLocator()->get(’console’); if (!$console instanceof Console) { throw new RuntimeException(’Cannot obtain console adapter. Are we running in a console?’) } } } If you are using ZendConsole without MVC, we can get adapter using the following code: 1 2 use ZendConsoleConsole; use ZendConsoleExceptionRuntimeException as ConsoleException; 3 4 try { 269
  • 308. Zend Framework 2 Documentation, Release 2.2.5 5 6 7 8 $console = Console::getInstance(); } catch (ConsoleException $e) { // Could not get console adapter - most likely we are not running inside a console window. } Note: For practical and security reasons, Console::getInstance() will always throw an exception if you attempt to get console instance in a non-console environment (i.e. when running on a HTTP server). You can override this behavior by manually instantiating one of ZendConsoleAdapter* classes. 58.2 Using console adapter 58.2.1 Window size and title $console->getWidth() (int) Get real console window width in characters. $console->getHeight() (int) Get real console window height in characters. $console->getSize() (array) Get an array( $width, $height) with current console window dimensions. $console->getTitle() (string) Get console window title. Note: For UTF-8 enabled consoles (terminals) dimensions represent the number of multibyte characters (real characters). Note: On consoles with virtual buffers (i.e. MS Windows Command Prompt) width and height represent visible (real) size, without scrolling the window. For example - if the window scrolling width is 120 chars, but it’s real, visible width is 80 chars, getWidth() will return 80. 58.2.2 Character set $console->isUtf8() (boolean) Is the console UTF-8 compatible (can display unicode strings) ? $console->getCharset() (ZendConsoleCharsetCharsetInterface) This method will return one of ConsoleCharset* classes that represent the readable charset that can be used for line-drawing. It is automatically detected by the adapter. 58.2.3 Writing to console $console->write( string $text, $color = null, $bgColor = null ) Write a $text to the console, optionally using foreground $color and background $bgColor. Color value is one of the constants in ZendConsoleColorInterface. $console->writeLine( string $text, $color = null, $bgColor = null ) Write a single line of $text to the console. This method will output a newline character at the end of text moving console cursor to next line. $console->writeAt( string $text, int $x, int $y, $color = null, $bgColor = null ) Write $text at the specified $x and $y coordinates of console window. Top left corner of the screen has coordinates of $x = 1; $x = 1. To retrieve far-right and bottom coordinates, use getWidth() and getHeight() methods. 270 Chapter 58. Console adapters
  • 309. Zend Framework 2 Documentation, Release 2.2.5 58.2.4 Reading from console $console->readChar( string $mask = null ) (string) Read a single character from console. Optional (string) $mask can be provided to force entering only a selected set of characters. For example, to read a single digit, we can use the following syntax: $digit = $console->readChar(’0123456789’); $console->readLine( int $maxLength = 2048 ) (string) Read a single line of input from console. Optional (int) $maxLength can be used to limit the length of data that will be read. The line will be returned without ending newline character. 58.2.5 Miscellaneous $console->hideCursor() Hide blinking cursor from console. $console->showCursor() Show blinking cursor in console. $console->clear() Clear the screen. $console->clearLine() Clear the line that the cursor currently sits at. 58.2. Using console adapter 271
  • 310. Zend Framework 2 Documentation, Release 2.2.5 272 Chapter 58. Console adapters
  • 311. CHAPTER 59 Console prompts In addition to console abstraction layer Zend Framework 2 provides numerous convenience classes for interacting with the user in console environment. This chapter describes available ZendConsolePrompt classes and their example usage. All prompts can be instantiated as objects and provide show() method. 1 use ZendConsolePrompt; 2 3 4 5 6 7 $confirm = new PromptConfirm(’Are you sure you want to continue?’); $result = $confirm->show(); if ($result) { // the user chose to continue } There is also a shorter method of displaying prompts, using static prompt() method: 1 use ZendConsolePrompt; 2 3 4 5 6 $result = PromptConfirm::prompt(’Are you sure you want to continue?’); if ($result) { // the user chose to continue } Both of above examples will display something like this: See Also: Make sure to read about console MVC integration first, because it provides a convenient way for running modular console applications without directly writing to or reading from console window. 273
  • 312. Zend Framework 2 Documentation, Release 2.2.5 59.1 Confirm This prompt is best used for a yes / no type of choices. Confirm( string $text, string $yesChar = ’y’, string $noChar = ’n’ ) $text (string) The text to show with the prompt $yesChar (string) The char that corresponds with YES choice. Defaults to y. $noChar (string) The char that corresponds with NO choice. Defaults to n. Example usage: use ZendConsolePromptConfirm; if ( Confirm::prompt(’Is this the correct answer? [y/n]’, ’y’, ’n’) ) { $console->write("You chose YES"); } else { $console->write("You chose NO"); } 59.2 Line This prompt asks for a line of text input. Line( string $text = ’Please enter value’, bool $allowEmpty = false, bool $maxLength = 2048 ) $text (string) The text to show with the prompt $allowEmpty (boolean) Can this prompt be skipped, by pressing [ENTER] ? (default fo false) $maxLength (integer) Maximum length of the input. Anything above this limit will be truncated. Example usage: use ZendConsolePromptLine; $name = Line::prompt( ’What is your name?’, false, 100 274 Chapter 59. Console prompts
  • 313. Zend Framework 2 Documentation, Release 2.2.5 ); $console->write("Good day to you $name!"); 59.3 Char This prompt reads a single keystroke and optionally validates it against a list o allowed characters. Char( string string bool bool bool ) $text = ’Please hit a key’, $allowedChars = ’abc’, $ignoreCase = true, $allowEmpty = false, $echo = true $text (string) The text to show with the prompt $allowedChars (string) A list of allowed keys that can be pressed. $ignoreCase (boolean) Ignore the case of chars pressed (default to true) $allowEmpty (boolean) Can this prompt be skipped, by pressing [ENTER] ? (default fo false) $echo (boolean) Should the selection be displayed on the screen ? Example usage: use ZendConsolePromptChar; $answer = Char::prompt( ’What is the correct answer? [a,b,c,d,e]’, ’abcde’, true, false, true ); if ($answer == ’b’) { $console->write(’Correct. This it the right answer’); } else { $console->write(’Wrong ! Try again.’); } 59.3. Char 275
  • 314. Zend Framework 2 Documentation, Release 2.2.5 59.4 Select This prompt displays a number of choices and asks the user to pick one. Select( string array bool bool ) $text = ’Please select one option’, $options = array(), $allowEmpty = false, $echo = false $text (string) The text to show with the prompt $options (array) An associative array with keys strokes (chars) and their displayed values. $allowEmpty (boolean) Can this prompt be skipped, by pressing [ENTER] ? (default fo false) $echo (boolean) Should the selection be displayed on the screen ? Example usage: $options = ’a’ => ’o’ => ’p’ => ’b’ => ’n’ => ); array( ’Apples’, ’Oranges’, ’Pears’, ’Bananas’, ’none of the above...’ $answer = Select::prompt( ’Which fruit do you like the best?’, $options, false, false ); $console->write("You told me that you like " . $options[$answer]); 276 Chapter 59. Console prompts
  • 315. Zend Framework 2 Documentation, Release 2.2.5 See Also: To learn more about accessing console, writing to and reading from it, make sure to read the following chapter: Console adapters. 59.4. Select 277
  • 316. Zend Framework 2 Documentation, Release 2.2.5 278 Chapter 59. Console prompts
  • 317. CHAPTER 60 Introduction to ZendCrypt ZendCrypt provides support of some cryptographic tools. The available features are: • encrypt-then-authenticate using symmetric ciphers (the authentication step is provided using HMAC); • encrypt/decrypt using symmetric and public key algorithm (e.g. RSA algorithm); • generate digital sign using public key algorithm (e.g. RSA algorithm); • key exchange using the Diffie-Hellman method; • Key derivation function (e.g. using PBKDF2 algorithm); • Secure password hash (e.g. using Bcrypt algorithm); • generate Hash values; • generate HMAC values; The main scope of this component is to offer an easy and secure way to protect and authenticate sensitive data in PHP. Because the use of cryptography is not so easy we recommend to use the ZendCrypt component only if you have a minimum background on this topic. For an introduction to cryptography we suggest the following references: • Dan Boneh “Cryptography course” Stanford University, Coursera - free online course • N.Ferguson, B.Schneier, and T.Kohno, “Cryptography Engineering”, John Wiley & Sons (2010) • B.Schneier “Applied Cryptography”, John Wiley & Sons (1996) Note: PHP-CryptLib Most of the ideas behind the ZendCrypt component have been inspired by the PHP-CryptLib project of Anthony Ferrara. PHP-CryptLib is an all-inclusive pure PHP cryptographic library for all cryptographic needs. It is meant to be easy to install and use, yet extensible and powerful enough for even the most experienced developer. 279
  • 318. Zend Framework 2 Documentation, Release 2.2.5 280 Chapter 60. Introduction to ZendCrypt
  • 319. CHAPTER 61 Encrypt/decrypt using block ciphers ZendCryptBlockCipher implements the encrypt-then-authenticate mode using HMAC to provide authentication. The symmetric cipher can be choose with a specific adapter that implements the ZendCryptSymmetricSymmetricInterface. We support the standard algorithms of the Mcrypt extension. The adapter that implements the Mcrypt is ZendCryptSymmetricMcrypt. In the following code we reported an example on how to use the BlockCipher class to encrypt-then-authenticate a string using the AES block cipher (with a key of 256 bit) and the HMAC algorithm (using the SHA-256 hash function). 1 use ZendCryptBlockCipher; 2 3 4 5 6 $blockCipher = BlockCipher::factory(’mcrypt’, array(’algo’ => ’aes’)); $blockCipher->setKey(’encryption key’); $result = $blockCipher->encrypt(’this is a secret message’); echo "Encrypted text: $result n"; The BlockCipher is initialized using a factory method with the name of the cipher adapter to use (mcrypt) and the parameters to pass to the adapter (the AES algorithm). In order to encrypt a string we need to specify an encryption key and we used the setKey() method for that scope. The encryption is provided by the encrypt() method. The output of the encryption is a string, encoded in Base64 (default), that contains the HMAC value, the IV vector, and the encrypted text. The encryption mode used is the CBC (with a random IV by default) and SHA256 as default hash algorithm of the HMAC. The Mcrypt adapter encrypts using the PKCS#7 padding mechanism by default. You can specify a different padding method using a special adapter for that (ZendCryptSymmetricPadding). The encryption and authentication keys used by the BlockCipher are generated with the PBKDF2 algorithm, used as key derivation function from the user’s key specified using the setKey() method. Note: Key size BlockCipher try to use always the longest size of the key for the specified cipher. For instance, for the AES algorithm it uses 256 bits and for the Blowfish algorithm it uses 448 bits. You can change all the default settings passing the values to the factory parameters. For instance, if you want to use the Blowfish algorithm, with the CFB mode and the SHA512 hash function for HMAC you have to initialize the class as follow: 1 use ZendCryptBlockCipher; 2 281
  • 320. Zend Framework 2 Documentation, Release 2.2.5 3 4 5 6 7 $blockCipher = BlockCipher::factory(’mcrypt’, array( ’algo’ => ’blowfish’, ’mode’ => ’cfb’, ’hash’ => ’sha512’ )); Note: Recommendation If you are not familiar with symmetric encryption techniques we strongly suggest to use the default values of the BlockCipher class. The default values are: AES algorithm, CBC mode, HMAC with SHA256, PKCS#7 padding. To decrypt a string we can use the decrypt() method. In order to successfully decrypt a string we have to configure the BlockCipher with the same parameters of the encryption. We can also initialize the BlockCipher manually without use the factory method. We can inject the symmetric cipher adapter directly to the constructor of the BlockCipher class. For instance, we can rewrite the previous example as follow: 1 2 use ZendCryptBlockCipher; use ZendCryptSymmetricMcrypt; 3 4 5 6 7 $blockCipher = new BlockCipher(new Mcrypt(array(’algo’ => ’aes’))); $blockCipher->setKey(’encryption key’); $result = $blockCipher->encrypt(’this is a secret message’); echo "Encrypted text: $result n"; 282 Chapter 61. Encrypt/decrypt using block ciphers
  • 321. CHAPTER 62 Key derivation function In cryptography, a key derivation function (or KDF) derives one or more secret keys from a secret value such as a master key or other known information such as a password or passphrase using a pseudo-random function. For instance, a KDF function can be used to generate encryption or authentication keys from a user password. The ZendCryptKeyDerivation implements a key derivation function using specific adapters. User passwords are not really suitable to be used as keys in cryptographic algorithms, since users normally choose keys they can write on keyboard. These passwords use only 6 to 7 bits per character (or less). It is highly recommended to use always a KDF function to transform a user’s password in a cryptographic key. The output of the following key derivation functions is a binary string. If you need to store the value in a database or a different persistent storage, we suggest to convert it in Base64 format, using base64_encode() function, or in hex format, using the bin2hex() function. 62.1 Pbkdf2 adapter Pbkdf2 is a KDF that applies a pseudorandom function, such as a cryptographic hash, to the input password or passphrase along with a salt value and repeats the process many times to produce a derived key, which can then be used as a cryptographic key in subsequent operations. The added computational work makes password cracking much more difficult, and is known as key stretching. In the example below we show a typical usage of the Pbkdf2 adapter. 1 2 use ZendCryptKeyDerivationPbkdf2; use ZendMathRand; 3 4 5 6 $pass = ’password’; $salt = Rand::getBytes(strlen($pass), true); $key = Pbkdf2::calc(’sha256’, $pass, $salt, 10000, strlen($pass)*2); 7 8 9 printf ("Original password: %sn", $pass); printf ("Derived key (hex): %sn", bin2hex($key)); The Pbkdf2 adapter takes the password ($pass) and generate a binary key with a size double of the password. The syntax is calc($hash, $pass, $salt, $iterations, $length) where $hash is the name of the hash function to use, $pass is the password, $salt is a pseudo random value, $iterations is the number of iterations of the algorithm and $length is the size of the key to be generated. We used the Rand::getBytes function of 283
  • 322. Zend Framework 2 Documentation, Release 2.2.5 the ZendMathRand class to generate a random bytes using a strong generators (the true value means the usage of strong generators). The number of iterations is a very important parameter for the security of the algorithm. Big values means more security. There is not a fixed value for that because the number of iterations depends on the CPU power. You should always choose a number of iteration that prevent brute force attacks. For instance, a value of 1‘000‘000 iterations, that is equal to 1 sec of elaboration for the PBKDF2 algorithm, is enough secure using an Intel Core i5-2500 CPU at 3.3 Ghz. 62.2 SaltedS2k adapter The SaltedS2k algorithm uses an hash function and a salt to generate a key based on a user’s password. This algorithm doesn’t use a parameter that specify the number of iterations and for that reason it’s considered less secure compared with Pbkdf2. We suggest to use the SaltedS2k algorithm only if you really need it. Below is reported a usage example of the SaltedS2k adapter. 1 2 use ZendCryptKeyDerivationSaltedS2k; use ZendMathRand; 3 4 5 6 $pass = ’password’; $salt = Rand::getBytes(strlen($pass), true); $key = SaltedS2k::calc(’sha256’, $pass, $salt, strlen($pass)*2); 7 8 9 printf ("Original password: %sn", $pass); printf ("Derived key (hex): %sn", bin2hex($key)); 62.3 Scrypt adapter The scrypt algorithm uses the algorithm Salsa20/8 core and Pbkdf2-SHA256 to generate a key based on a user’s password. This algorithm has been designed to be more secure against hardware brute-force attacks than alternative functions such as Pbkdf2 or bcrypt. The scrypt algorithm is based on the idea of memory-hard algorithms and sequential memory-hard functions. A memory-hard algorithm is thus an algorithm which asymptotically uses almost as many memory locations as it uses operations[#f1]_. A natural way to reduce the advantage provided by an attackers ability to construct highly parallel circuits is to increase the size of a single key derivation circuit if a circuit is twice as large, only half as many copies can be placed on a given area of silicon while still operating within the resources available to software implementations, including a powerful CPU and large amounts of RAM. “From a test executed on modern (2009) hardware, if 5 seconds are spent computing a derived key, the cost of a hardware brute-force attack against scrypt is roughly 4000 times greater than the cost of a similar attack against bcrypt (to find the same password), and 20000 times greater than a similar attack against Pbkdf2.” Colin Percival (the author of scrypt algorithm) This algorithm uses 4 parameters to generate a key of 64 bytes: • salt, a random string; • N, the CPU cost; • r, the memory cost; • p, the parallelization cost. Below is reported a usage example of the Scrypt adapter. 284 Chapter 62. Key derivation function
  • 323. Zend Framework 2 Documentation, Release 2.2.5 1 2 use ZendCryptKeyDerivationScrypt; use ZendMathRand; 3 4 5 6 $pass = ’password’; $salt = Rand::getBytes(strlen($pass), true); $key = Scrypt::calc($pass, $salt, 2048, 2, 1, 64); 7 8 9 printf ("Original password: %sn", $pass); printf ("Derived key (hex): %sn", bin2hex($key)); Note: Performance of the scrypt implementation The aim of the scrypt algorithm is to generate secure derived key preventing brute force attacks. Just like the other derivation functions, the more time (and memory) we spent executing the algorithm, the more secure the derived key will be. Unfortunately a pure PHP implementation of the scrypt algorithm is very slow compared with the C implementation (this is always true, if you compare execution time of C with PHP). If you want use a faster scrypt algorithm we suggest to install the scrypt PECL extension. The Scrypt adapter of Zend Framework is able to recognize if the PECL extension is loaded and use it instead of the pure PHP implementation. 62.3. Scrypt adapter 285
  • 324. Zend Framework 2 Documentation, Release 2.2.5 286 Chapter 62. Key derivation function
  • 325. CHAPTER 63 Password In the ZendCryptPassword namespace you can find all the password formats supported by Zend Framework. We currently support the following passwords: • bcrypt; • Apache (htpasswd). If you need to choose a password format to store the user’s password we suggest to use the bcrypt algorithm that is considered secure against brute forcing attacks (see the details below). 63.1 Bcrypt The bcrypt algorithm is an hashing algorithm that is widely used and suggested by the security community to store users passwords in a secure way. Classic hashing mechanisms like MD5 or SHA, with or without a salt value, are not considered secure anymore (read this post to know why). The security of bcrypt is related to the speed of the algorithm. Bcrypt is very slow, it can request even a second to generate an hash value. That means a brute force attack is impossible to execute, due to the amount of time that its need. Bcrypt uses a cost parameter that specify the number of cycles to use in the algorithm. Increasing this number the algorithm will spend more time to generate the hash output. The cost parameter is represented by an integer value between 4 to 31. The default cost value of the ZendCryptPasswordBcrypt component is 14, that means almost a second using a CPU Intel i5 at 3.3Ghz (the cost parameter is a relative value according to the speed of the CPU used). If you want to change the cost parameter of the bcrypt algorithm you can use the setCost() method. Please note that if you change the cost parameter, the resulting hash will be different. This will not affect the verification process of the algorithm, therefore not breaking the password hashes you already have stored. Bcrypt reads the cost parameter from the hash value, during the password authentication. All of the parts needed to verify the hash are all together, separated with $’s, first the algorithm, then the cost, the salt, and then finally the hash. The example below shows how to use the bcrypt algorithm to store a user’s password: 1 use ZendCryptPasswordBcrypt; 2 287
  • 326. Zend Framework 2 Documentation, Release 2.2.5 3 4 $bcrypt = new Bcrypt(); $securePass = $bcrypt->create(’user password’); The output of the create() method is the hash of the password. This value can then be stored in a repository like a database (the output is a string of 60 bytes). To verify if a given password is valid against a bcrypt value you can use the verify() method. An example is reported below: 1 use ZendCryptPasswordBcrypt; 2 3 4 5 $bcrypt = new Bcrypt(); $securePass = ’the stored bcrypt value’; $password = ’the password to check’; 6 7 8 9 10 11 if ($bcrypt->verify($password, $securePass)) { echo "The password is correct! n"; } else { echo "The password is NOT correct.n"; } In the bcrypt uses also a salt value to improve the randomness of the algorithm. By default, the ZendCryptPasswordBcrypt component generates a random salt for each hash. If you want to specify a preselected salt you can use the setSalt() method. We provide also a getSalt() method to retrieve the salt specified by the user. The salt and the cost parameter can be also specified during the constructor of the class, below is reported an example: 1 use ZendCryptPasswordBcrypt; 2 3 4 5 6 $bcrypt = new Bcrypt(array( ’salt’ => ’random value’, ’cost’ => 13 )); Note: Bcrypt with non-ASCII passwords (8-bit characters) The bcrypt implementation used by PHP < 5.3.7 can contains a security flaw if the password uses 8-bit characters (here’s the security report). The impact of this bug was that most (but not all) passwords containing non-ASCII characters with the 8th bit set were hashed incorrectly, resulting in password hashes incompatible with those of OpenBSD’s original implementation of bcrypt. This security flaw has been fixed starting from PHP 5.3.7 and the prefix used in the output was changed to ‘$2y$’ in order to put evidence on the correctness of the hash value. If you are using PHP < 5.3.7 with 8-bit passwords, the ZendCryptPasswordBcrypt throws an exception suggesting to upgrade to PHP 5.3.7+ or use only 7-bit passwords. 63.2 Apache The ZendCryptPasswordApache supports all the password formats used by Apache (htpasswd). These formats are: • CRYPT, uses the traditional Unix crypt(3) function with a randomly-generated 32-bit salt (only 12 bits used) and the first 8 characters of the password; • SHA1, “{SHA}” + Base64-encoded SHA-1 digest of the password; 288 Chapter 63. Password
  • 327. Zend Framework 2 Documentation, Release 2.2.5 • MD5, “$apr1$” + the result of an Apache-specific algorithm using an iterated (1,000 times) MD5 digest of various combinations of a random 32-bit salt and the password. • Digest, the MD5 hash of the string user:realm:password as a 32-character string of hexadecimal digits. realm is the Authorization Realm argument to the AuthName directive in httpd.conf. In order to specify the format of the Apache’s password you can use the setFormat() method. An example with all the formats usage is reported below: 1 use ZendCryptPasswordApache; 2 3 $apache = new Apache(); 4 5 6 $apache->setFormat(’crypt’); printf ("CRYPT output: %sn", $apache->create(’password’)); 7 8 9 $apache->setFormat(’sha1’); printf ("SHA1 output: %sn", $apache->create(’password’)); 10 11 12 $apache->setFormat(’md5’); printf ("MD5 output: %sn", $apache->create(’password’)); 13 14 15 16 17 $apache->setFormat(’digest’); $apache->setUserName(’enrico’); $apache->setAuthName(’test’); printf ("Digest output: %sn", $apache->create(’password’)); You can also specify the format of the password during the constructor of the class: 1 use ZendCryptPasswordApache; 2 3 4 5 $apache = new Apache(array( ’format’ => ’md5’ )); Other possible parameters to pass in the constructor are username and authname, for the digest format. 63.2. Apache 289
  • 328. Zend Framework 2 Documentation, Release 2.2.5 290 Chapter 63. Password
  • 329. CHAPTER 64 Public key cryptography Public-key cryptography refers to a cryptographic system requiring two separate keys, one of which is secret and one of which is public. Although different, the two parts of the key pair are mathematically linked. One key locks or encrypts the plaintext, and the other unlocks or decrypts the cyphertext. Neither key can perform both functions. One of these keys is published or public, while the other is kept private. In Zend Framework we implemented two public key algorithms: Diffie-Hellman key exchange and RSA. 64.1 Diffie-Hellman The Diffie-Hellman algorithm is a specific method of exchanging cryptographic keys. It is one of the earliest practical examples of key exchange implemented within the field of cryptography. The DiffieHellman key exchange method allows two parties that have no prior knowledge of each other to jointly establish a shared secret key over an insecure communications channel. This key can then be used to encrypt subsequent communications using a symmetric key cipher. The diagram of operation of the Diffie-Hellman algorithm can be defined by the following picture (taken by the DiffieHellman Wikipedia page): The schema’s colors represent the parameters of the algorithm. Here is reported an example of usage using the ZendCryptPublicKeyDiffieHellman class: 1 use ZendCryptPublicKeyDiffieHellman; 2 3 4 5 6 7 8 9 10 11 12 13 $aliceOptions = array( ’prime’ => ’1551728981814736974712322577637155399157248019669154044797077953140576293785419175 ’4236981889937278161526466314385615958256881888899512721588426754199503412587065565 ’1048705376814767265132557470407658574792912915723345106432450947150072296210941943 ’984760375594985848253359305585439638443’, ’generator’=> ’2’, ’private’ => ’9920931406657259523640856959196798855714124956149426748625180803553539633227862014 ’81312712891672623072630995180324388841681491857745515696789091127409515009250358965 ’46342049838178521379132153348139908016819196219448310107072632515749339055798122538 ’04828702523796951800575031871051678091’ ); 14 15 16 17 $bobOptions = array( ’prime’ => $aliceOptions[’prime’], ’generator’=> ’2’, 291
  • 330. Zend Framework 2 Documentation, Release 2.2.5 292 Chapter 64. Public key cryptography
  • 331. Zend Framework 2 Documentation, Release 2.2.5 ’private’ 18 19 20 21 22 => ’3341173579263955862573363571789256361254818065040216115107747831484146370794889978 ’1232563473041055194677275288017786897281696355182174038670007603421340815392469256 ’6346473315660054548451083307242700347420706465071483108330449773716038209708335687 ’31616972608703322302585471319261275664’ ); 23 24 25 $alice = new DiffieHellman($aliceOptions[’prime’], $aliceOptions[’generator’], $aliceOptions[’private $bob = new DiffieHellman($bobOptions[’prime’], $bobOptions[’generator’], $bobOptions[’private’]); 26 27 28 $alice->generateKeys(); $bob->generateKeys(); 29 30 31 32 $aliceSecretKey = $alice->computeSecretKey($bob->getPublicKey(DiffieHellman::FORMAT_BINARY), DiffieHellman::FORMAT_BINARY, DiffieHellman::FORMAT_BINARY); 33 34 $bobSecretKey 35 36 = $bob->computeSecretKey($alice->getPublicKey(DiffieHellman::FORMAT_BINARY), DiffieHellman::FORMAT_BINARY, DiffieHellman::FORMAT_BINARY); 37 38 39 40 41 42 if ($aliceSecretKey !== $bobSecretKey) { echo "ERROR!n"; } else { printf("The secret key is: %sn", base64_encode($aliceSecretKey)); } The parameters of the Diffie-Hellman class are: a prime number (p), a generator (g) that is a primitive root mod p and a private integer number. The security of the Diffie-Hellman exchange algorithm is related to the choice of these parameters. To know how to choose secure numbers you can read the RFC 3526 document. Note: The ZendCryptPublicKeyDiffieHellman class use by default the OpenSSL extension of PHP to generate the parameters. If you don’t want to use the OpenSSL library you have to set the useOpensslExtension static method to false. 64.2 RSA RSA is an algorithm for public-key cryptography that is based on the presumed difficulty of factoring large integers, the factoring problem. A user of RSA creates and then publishes the product of two large prime numbers, along with an auxiliary value, as their public key. The prime factors must be kept secret. Anyone can use the public key to encrypt a message, but with currently published methods, if the public key is large enough, only someone with knowledge of the prime factors can feasibly decode the message. Whether breaking RSA encryption is as hard as factoring is an open question known as the RSA problem. The RSA algorithm can be used to encrypt/decrypt message and also to provide authenticity and integrity generating a digital signature of a message. Suppose that Alice wants to send an encrypted message to Bob. Alice must use the public key of Bob to encrypt the message. Bob can decrypt the message using his private key. Because Bob he is the only one that can access to his private key, he is the only one that can decrypt the message. If Alice wants to provide authenticity and integrity of a message to Bob she can use her private key to sign the message. Bob can check the correctness of the digital signature using the public key of Alice. Alice can provide encryption, authenticity and integrity of a message to Bob using the previous schemas in sequence, applying the encryption first and the digital signature after. Below we reported some examples of usage of the ZendCryptPublicKeyRsa class in order to: • generate a public key and a private key; 64.2. RSA 293
  • 332. Zend Framework 2 Documentation, Release 2.2.5 • encrypt/decrypt a string; • generate a digital signature of a file. 64.2.1 Generate a public key and a private key In order to generate a public and private key you can use the following code: 1 use ZendCryptPublicKeyRsaOptions; 2 3 4 5 $rsaOptions = new RsaOptions(array( ’pass_phrase’ => ’test’ )); 6 7 8 9 $rsaOptions->generateKeys(array( ’private_key_bits’ => 2048, )); 10 11 12 file_put_contents(’private_key.pem’, $rsaOptions->getPrivateKey()); file_put_contents(’public_key.pub’, $rsaOptions->getPublicKey()); This example generates a public and private key of 2048 bit storing the keys in two separate files, the private_key.pem for the private key and the public_key.pub for the public key. You can also generate the public and private key using OpenSSL from the command line (Unix style syntax): ssh-keygen -t rsa 64.2.2 Encrypt and decrypt a string Below is reported an example on how to encrypt and decrypt a string using the RSA algorithm. You can encrypt only small strings. The maximum size of encryption is given by the length of the public/private key - 88 bits. For instance, if we use a size of 2048 bit you can encrypt string with a maximum size of 1960 bit (245 characters). This limitation is related to the OpenSSL implementation for a security reason related to the nature of the RSA algorithm. The normal application of a public key encryption algorithm is to store a key or a hash of the data you want to respectively encrypt or sign. A hash is typically 128-256 bits (the PHP sha1() function returns a 160 bit hash). An AES encryption key is 128 to 256 bits. So either of those will comfortably fit inside a single RSA encryption. 1 use ZendCryptPublicKeyRsa; 2 3 4 5 6 7 8 $rsa = Rsa::factory(array( ’public_key’ => ’public_key.pub’, ’private_key’ => ’private_key.pem’, ’pass_phrase’ => ’test’, ’binary_output’ => false )); 9 10 $text = ’This is the message to encrypt’; 11 12 13 $encrypt = $rsa->encrypt($text); printf("Encrypted message:n%sn", $encrypt); 14 15 $decrypt = $rsa->decrypt($encrypt); 16 17 18 19 if ($text !== $decrypt) { echo "ERRORn"; } else { 294 Chapter 64. Public key cryptography
  • 333. Zend Framework 2 Documentation, Release 2.2.5 echo "Encryption and decryption performed successfully!n"; 20 21 } 64.2.3 Generate a digital signature of a file Below is reported an example of how to generate a digital signature of a file. 1 use ZendCryptPublicKeyRsa; 2 3 4 5 6 7 $rsa = Rsa::factory(array( ’private_key’ => ’path/to/private_key’, ’pass_phrase’ => ’passphrase of the private key’, ’binary_output’ => false )); 8 9 $file = file_get_contents(’path/file/to/sign’); 10 11 12 $signature = $rsa->sign($file, $rsa->getOptions()->getPrivateKey()); $verify = $rsa->verify($file, $signature, $rsa->getOptions()->getPublicKey()); 13 14 15 16 17 18 19 20 if ($verify) { echo "The signature is OKn"; file_put_contents($filename . ’.sig’, $signature); echo "Signature save in $filename.sign"; } else { echo "The signature is not valid!n"; } In this example we used the Base64 format to encode the digital signature of the file (binary_output is false). Note: The implementation of ZendCryptPublicKeyRsa algorithm uses the OpenSSL extension of PHP. 64.2. RSA 295
  • 334. Zend Framework 2 Documentation, Release 2.2.5 296 Chapter 64. Public key cryptography
  • 335. CHAPTER 65 ZendDbAdapter The Adapter object is the most important sub-component of ZendDb. It is responsible for adapting any code written in or for ZendDb to the targeted php extensions and vendor databases. In doing this, it creates an abstraction layer for the PHP extensions, which is called the “Driver” portion of the ZendDb adapter. It also creates a lightweight abstraction layer, called the “Platform” portion of the adapter, for the various idiosyncrasies that each vendor-specific platform might have in its SQL/RDBMS implementation. 65.1 Creating an Adapter - Quickstart Creating an adapter can simply be done by instantiating the ZendDbAdapterAdapter class. The most common use case, while not the most explicit, is to pass an array of configuration to the Adapter. 1 $adapter = new ZendDbAdapterAdapter($configArray); This driver array is an abstraction for the extension level required parameters. Here is a table for the key-value pairs that should be in configuration array. Key driver database username password hostname port charset Is Required? required generally required generally required generally required not generally required not generally required not generally required Value Mysqli, Sqlsrv, Pdo_Sqlite, Pdo_Mysql, Pdo=OtherPdoDriver the name of the database (schema) the connection username the connection password the IP address or hostname to connect to the port to connect to (if applicable) the character set to use Note: Other names will work as well. Effectively, if the PHP manual uses a particular naming, this naming will be supported by our Driver. For example, dbname in most cases will also work for ‘database’. Another example is that in the case of Sqlsrv, UID will work in place of username. Which format you chose is up to you, but the above table represents the official abstraction names. So, for example, a MySQL connection using ext/mysqli: 1 2 3 4 $adapter = new ZendDbAdapterAdapter(array( ’driver’ => ’Mysqli’, ’database’ => ’zend_db_example’, ’username’ => ’developer’, 297
  • 336. Zend Framework 2 Documentation, Release 2.2.5 5 6 ’password’ => ’developer-password’ )); Another example, of a Sqlite connection via PDO: 1 2 3 4 $adapter = new ZendDbAdapterAdapter(array( ’driver’ => ’Pdo_Sqlite’, ’database’ => ’path/to/sqlite.db’ )); It is important to know that by using this style of adapter creation, the Adapter will attempt to create any dependencies that were not explicitly provided. A Driver object will be created from the configuration array provided in the constructor. A Platform object will be created based off the type of Driver class that was instantiated. And lastly, a default ResultSet object is created and utilized. Any of these objects can be injected, to do this, see the next section. The list of officially supported drivers: • Mysqli: The ext/mysqli driver • Pgsql: The ext/pgsql driver • Sqlsrv: The ext/sqlsrv driver (from Microsoft) • Pdo_Mysql: MySQL through the PDO extension • Pdo_Sqlite: SQLite though the PDO extension • Pdo_Pgsql: PostgreSQL through the PDO extension 65.2 Creating an Adapter Using Dependency Injection The more expressive and explicit way of creating an adapter is by injecting all your dependencies up front. ZendDbAdapterAdapter uses constructor injection, and all required dependencies are injected through the constructor, which has the following signature (in pseudo-code): 1 2 use ZendDbAdapterPlatformPlatformInterface; use ZendDbResultSetResultSet; 3 4 5 6 class ZendDbAdapterAdapter { public function __construct($driver, PlatformInterface $platform = null, ResultSet $queryResultSe } What can be injected: • $driver an array of connection parameters ZendDbAdapterDriverDriverInterface (see above) or an instance of • $platform - (optional) an instance of ZendDbPlatformPlatformInterface, the default will be created based off the driver implementation • $queryResultSetPrototype - (optional) an instance of ZendDbResultSetResultSet, to understand this object’s role, see the section below on querying through the adapter 65.3 Query Preparation Through ZendDbAdapterAdapter::query() By default, query() prefers that you use “preparation” as a means for processing SQL statements. This generally means that you will supply a SQL statement with the values substituted by placeholders, and then the parameters for those placeholders are supplied separately. An example of this workflow with ZendDbAdapterAdapter is: 298 Chapter 65. ZendDbAdapter
  • 337. Zend Framework 2 Documentation, Release 2.2.5 1 $adapter->query(’SELECT * FROM ‘artist‘ WHERE ‘id‘ = ?’, array(5)); The above example will go through the following steps: • create a new Statement object • prepare an array into a ParameterContainer if necessary • inject the ParameterContainer into the Statement object • execute the Statement object, producing a Result object • check the Result object to check if the supplied sql was a “query”, or a result set producing statement • if it is a result set producing query, clone the ResultSet prototype, inject Result as datasource, return it • else, return the Result 65.4 Query Execution Through ZendDbAdapterAdapter::query() In some cases, you have to execute statements directly. The primary purpose for needing to execute sql instead of prepare and execute a sql statement, might be because you are attempting to execute a DDL statement (which in most extensions and vendor platforms), are un-preparable. An example of executing: 1 $adapter->query(’ALTER TABLE ADD INDEX(‘foo_index‘) ON (‘foo_column‘)’, Adapter::QUERY_MODE_EXECUTE); The primary difference to notice is that you must provide the Adapter::QUERY_MODE_EXECUTE (execute) as the second parameter. 65.5 Creating Statements While query() is highly useful for one-off and quick querying of a database through Adapter, it generally makes more sense to create a statement and interact with it directly, so that you have greater control over the prepare-then-execute workflow. To do this, Adapter gives you a routine called createStatement() that allows you to create a Driver specific Statement to use so you can manage your own prepare-then-execute workflow. 1 2 3 // with optional parameters to bind up-front $statement = $adapter->createStatement($sql, $optionalParameters); $result = $statement->execute(); 65.6 Using the Driver Object The Driver object is the primary place where ZendDbAdapterAdapter implements the connection level abstraction making it possible to use all of ZendDb’s interfaces via the various ext/mysqli, ext/sqlsrv, PDO, and other PHP level drivers. To make this possible, each driver is composed of 3 objects: • A connection: ZendDbAdapterDriverConnectionInterface • A statement: ZendDbAdapterDriverStatementInterface • A result: ZendDbAdapterDriverResultInterface Each of the built-in drivers practices “prototyping” as a means of creating objects when new instances are requested. The workflow looks like this: • An adapter is created with a set of connection parameters 65.4. Query Execution Through ZendDbAdapterAdapter::query() 299
  • 338. Zend Framework 2 Documentation, Release 2.2.5 • The adapter chooses the proper driver to instantiate, for example ZendDbAdapterDriverMysqli • That driver class is instantiated • If no connection, statement or result objects are injected, defaults are instantiated This driver is now ready to be called on when particular workflows are requested. Here is what the Driver API looks like: 1 namespace ZendDbAdapterDriver; 2 interface DriverInterface { const PARAMETERIZATION_POSITIONAL = ’positional’; const PARAMETERIZATION_NAMED = ’named’; const NAME_FORMAT_CAMELCASE = ’camelCase’; const NAME_FORMAT_NATURAL = ’natural’; public function getDatabasePlatformName($nameFormat = self::NAME_FORMAT_CAMELCASE); public function checkEnvironment(); public function getConnection(); public function createStatement($sqlOrResource = null); public function createResult($resource); public function getPrepareType(); public function formatParameterName($name, $type = null); public function getLastGeneratedValue(); } 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 From this DriverInterface, you can • Determine the name of the platform this driver supports (useful for choosing the proper platform object) • Check that the environment can support this driver • Return the Connection object • Create a Statement object which is optionally seeded by an SQL statement (this will generally be a clone of a prototypical statement object) • Create a Result object which is optionally seeded by a statement resource (this will generally be a clone of a prototypical result object) • Format parameter names, important to distinguish the difference between the various ways parameters are named between extensions • Retrieve the overall last generated value (such as an auto-increment value) Statement objects generally look like this: 1 namespace ZendDbAdapterDriver; 2 3 4 5 6 7 8 interface StatementInterface extends StatementContainerInterface { public function getResource(); public function prepare($sql = null); public function isPrepared(); public function execute($parameters = null); 9 /** Inherited from StatementContainerInterface */ public function setSql($sql); public function getSql(); public function setParameterContainer(ParameterContainer $parameterContainer); public function getParameterContainer(); 10 11 12 13 14 15 } 300 Chapter 65. ZendDbAdapter
  • 339. Zend Framework 2 Documentation, Release 2.2.5 Result objects generally look like this: 1 namespace ZendDbAdapterDriver; 2 3 4 5 6 7 8 9 10 11 interface ResultInterface extends Countable, Iterator { public function buffer(); public function isQueryResult(); public function getAffectedRows(); public function getGeneratedValue(); public function getResource(); public function getFieldCount(); } 65.7 Using The Platform Object The Platform object provides an API to assist in crafting queries in a way that is specific to the SQL implementation of a particular vendor. Nuances such as how identifiers or values are quoted, or what the identifier separator character is are handled by this object. To get an idea of the capabilities, the interface for a platform object looks like this: 1 namespace ZendDbAdapterPlatform; 2 3 4 5 6 7 8 9 10 11 12 13 14 interface PlatformInterface { public function getName(); public function getQuoteIdentifierSymbol(); public function quoteIdentifier($identifier); public function quoteIdentifierChain($identiferChain) public function getQuoteValueSymbol(); public function quoteValue($value); public function quoteValueList($valueList); public function getIdentifierSeparator(); public function quoteIdentifierInFragment($identifier, array $additionalSafeWords = array()); } While one can instantiate your own Platform object, generally speaking, it is easier to get the proper Platform instance from the configured adapter (by default the Platform type will match the underlying driver implementation): 1 2 3 $platform = $adapter->getPlatform(); // or $platform = $adapter->platform; // magic property access The following is a couple of example of Platform usage: 1 2 3 /** @var $adapter ZendDbAdapterAdapter */ /** @var $platform ZendDbAdapterPlatformSql92 */ $platform = $adapter->getPlatform(); 4 5 6 // "first_name" echo $platform->quoteIdentifier(’first_name’); 7 8 9 // " echo $platform->getQuoteIdentifierSymbol(); 10 11 12 // "schema"."mytable" echo $platform->quoteIdentifierChain(array(’schema’,’mytable’))); 13 65.7. Using The Platform Object 301
  • 340. Zend Framework 2 Documentation, Release 2.2.5 14 15 // ’ echo $platform->getQuoteValueSymbol(); 16 17 18 // ’myvalue’ echo $platform->quoteValue(’myvalue’); 19 20 21 // ’value’, ’Foo O’Bar’ echo $platform->quoteValueList(array(’value’,"Foo O’Bar"))); 22 23 24 // . echo $platform->getIdentifierSeparator(); 25 26 27 // "foo" as "bar" echo $platform->quoteIdentifierInFragment(’foo as bar’); 28 29 30 31 // additionally, with some safe words: // ("foo"."bar" = "boo"."baz") echo $platform->quoteIdentifierInFragment(’(foo.bar = boo.baz)’, array(’(’, ’)’, ’=’)); 65.8 Using The Parameter Container The ParameterContainer object is a container for the various parameters that need to be passed into a Statement object to fulfill all the various parameterized parts of the SQL statement. This object implements the ArrayAccess interface. Below is the ParameterContainer API: namespace ZendDbAdapter; class ParameterContainer implements Iterator, ArrayAccess, Countable { public function __construct(array $data = array()) /** methods to interact with values */ public function offsetExists($name) public function offsetGet($name) public function offsetSetReference($name, $from) public function offsetSet($name, $value, $errata = null) public function offsetUnset($name) /** set values from array (will reset first) */ public function setFromArray(Array $data) /** methods to interact with value errata */ public function offsetSetErrata($name, $errata) public function offsetGetErrata($name) public function offsetHasErrata($name) public function offsetUnsetErrata($name) /** errata only iterator */ public function getErrataIterator() /** get array with named keys */ public function getNamedArray() /** get array with int keys, ordered by position */ public function getPositionalArray() /** iterator: */ 302 Chapter 65. ZendDbAdapter
  • 341. Zend Framework 2 Documentation, Release 2.2.5 public public public public public public function function function function function function count() current() next() key() valid() rewind() /** merge existing array of parameters with existing parameters */ public function merge($parameters) } In addition to handling parameter names and values, the container will assist in tracking parameter types for PHP type to SQL type handling. For example, it might be important that: $container->offsetSet(’limit’, 5); be bound as an integer. To achieve this, pass in the ParameterContainer::TYPE_INTEGER constant as the 3rd parameter: $container->offsetSet(’limit’, 5, $container::TYPE_INTEGER); This will ensure that if the underlying driver supports typing of bound parameters, that this translated information will also be passed along to the actual php database driver. 65.9 Examples Creating a Driver and Vendor portable Query, Preparing and Iterating Result 1 $adapter = new ZendDbAdapterAdapter($driverConfig); 2 3 4 $qi = function($name) use ($adapter) { return $adapter->platform->quoteIdentifier($name); }; $fp = function($name) use ($adapter) { return $adapter->driver->formatParameterName($name); }; 5 6 7 8 $sql = ’UPDATE ’ . $qi(’artist’) . ’ SET ’ . $qi(’name’) . ’ = ’ . $fp(’name’) . ’ WHERE ’ . $qi(’id’) . ’ = ’ . $fp(’id’); 9 10 11 /** @var $statement ZendDbAdapterDriverStatementInterface */ $statement = $adapter->query($sql); 12 13 14 15 16 $parameters = array( ’name’ => ’Updated Artist’, ’id’ => 1 ); 17 18 $statement->execute($parameters); 19 20 // DATA INSERTED, NOW CHECK 21 22 23 24 25 /* @var $statement ZendDbAdapterDriverStatementInterface */ $statement = $adapter->query(’SELECT * FROM ’ . $qi(’artist’) . ’ WHERE id = ’ . $fp(’id’)); 26 27 28 /* @var $results ZendDbResultSetResultSet */ $results = $statement->execute(array(’id’ => 1)); 29 65.9. Examples 303
  • 342. Zend Framework 2 Documentation, Release 2.2.5 30 31 $row = $results->current(); $name = $row[’name’]; 304 Chapter 65. ZendDbAdapter
  • 343. CHAPTER 66 ZendDbResultSet ZendDbResultSet is a sub-component of ZendDb for abstracting the iteration of rowset producing queries. While data sources for this can be anything that is iterable, generally a ZendDbAdapterDriverResultInterface based object is the primary source for retrieving data. ZendDbResultSet‘s must implement the ZendDbResultSetResultSetInterface and all subcomponents of ZendDb that return a ResultSet as part of their API will assume an instance of a ResultSetInterface should be returned. In most casts, the Prototype pattern will be used by consuming object to clone a prototype of a ResultSet and return a specialized ResultSet with a specific data source injected. The interface of ResultSetInterface looks like this: 1 2 3 4 5 interface ResultSetInterface extends Traversable, Countable { public function initialize($dataSource); public function getFieldCount(); } 66.1 Quickstart ZendDbResultSetResultSet is the most basic form of a ResultSet object that will expose each row as either an ArrayObject-like object or an array of row data. By default, ZendDbAdapterAdapter will use a prototypical ZendDbResultSetResultSet object for iterating when using the ZendDbAdapterAdapter::query() method. The following is an example workflow ZendDbAdapterAdapter::query(): 1 2 similar to what one might find inside use ZendDbAdapterDriverResultInterface; use ZendDbResultSetResultSet; 3 4 5 6 $stmt = $driver->createStatement(’SELECT * FROM users’); $stmt->prepare(); $result = $stmt->execute($parameters); 7 8 9 10 if ($result instanceof ResultInterface && $result->isQueryResult()) { $resultSet = new ResultSet; $resultSet->initialize($result); 11 305
  • 344. Zend Framework 2 Documentation, Release 2.2.5 foreach ($resultSet as $row) { echo $row->my_column . PHP_EOL; } 12 13 14 15 } 66.2 ZendDbResultSetResultSet and ZendDbResultSetAbstractResultSet For most purposes, either a instance of ZendDbResultSetResultSet or a derivative of ZendDbResultSetAbstractResultSet will be being used. The implementation of the AbstractResultSet offers the following core functionality: 1 2 3 4 5 abstract class AbstractResultSet implements Iterator, ResultSetInterface { public function initialize($dataSource) public function getDataSource() public function getFieldCount() 6 /** Iterator */ public function public function public function public function public function 7 8 9 10 11 12 next() key() current() valid() rewind() 13 /** countable */ public function count() 14 15 16 /** get rows as array */ public function toArray() 17 18 19 } 66.3 ZendDbResultSetHydratingResultSet ZendDbResultSetHydratingResultSet is a more flexible ResultSet object that allows the developer to choose an appropriate “hydration strategy” for getting row data into a target object. While iterating over results, HydratingResultSet will take a prototype of a target object and clone it once for each row. The HydratingResultSet will then hydrate that clone with the row data. In the example below, rows from the database will be iterated, and during iteration, HydratingRowSet will use the Reflection based hydrator to inject the row data directly into the protected members of the cloned UserEntity object: 1 2 3 use ZendDbAdapterDriverResultInterface; use ZendDbResultSetHydratingResultSet; use ZendStdlibHydratorReflection as ReflectionHydrator; 4 5 6 7 8 9 10 11 12 class UserEntity { protected $first_name; protected $last_name; public function getFirstName() { return $this->first_name; } public function getLastName() { return $this->last_name; } public function setFirstName($first_name) { $this->first_name = $first_name; } public function setLastName($last_name) { $this->last_name = $last_name; } } 13 306 Chapter 66. ZendDbResultSet
  • 345. Zend Framework 2 Documentation, Release 2.2.5 14 15 16 $stmt = $driver->createStatement($sql); $stmt->prepare($parameters); $result = $stmt->execute(); 17 18 19 20 if ($result instanceof ResultInterface && $result->isQueryResult()) { $resultSet = new HydratingResultSet(new ReflectionHydrator, new UserEntity); $resultSet->initialize($result); 21 foreach ($resultSet as $user) { echo $user->getFirstName() . ’ ’ . $user->getLastName() . PHP_EOL; } 22 23 24 25 } For more information, see the ZendStdlibHydrator documentation to get a better sense of the different strategies that can be employed in order to populate a target object. 66.3. ZendDbResultSetHydratingResultSet 307
  • 346. Zend Framework 2 Documentation, Release 2.2.5 308 Chapter 66. ZendDbResultSet
  • 347. CHAPTER 67 ZendDbSql ZendDbSql is a SQL abstraction layer for building platform specific SQL queries via a object-oriented API. The end result of an ZendDbSql object will be to either produce a Statement and Parameter container that represents the target query, or a full string that can be directly executed against the database platform. To achieve this, ZendDbSql objects require a ZendDbAdapterAdapter object in order to produce the desired results. 67.1 ZendDbSqlSql (Quickstart) As there are four primary tasks associated with interacting with a database (from the DML, or Data Manipulation Language): selecting, inserting, updating and deleting. As such, there are four primary objects that developers can interact or building queries, ZendDbSqlSelect, Insert, Update and Delete. Since these four tasks are so closely related, and generally used together within the same application, ZendDbSqlSql objects help you create them and produce the result you are attempting to achieve. 1 2 3 4 5 6 use ZendDbSqlSql; $sql = new Sql($adapter); $select = $sql->select(); $insert = $sql->insert(); $update = $sql->update(); $delete = $sql->delete(); // // // // @return @return @return @return ZendDbSqlSelect ZendDbSqlInsert ZendDbSqlUpdate ZendDbSqlDelete As a developer, you can now interact with these objects, as described in the sections below, to specialize each query. Once they have been populated with values, they are ready to either be prepared or executed. To prepare (using a Select object): 1 2 3 4 5 use ZendDbSqlSql; $sql = new Sql($adapter); $select = $sql->select(); $select->from(’foo’); $select->where(array(’id’ => 2)); 6 7 8 $statement = $sql->prepareStatementForSqlObject($select); $results = $statement->execute(); To execute (using a Select object) 309
  • 348. Zend Framework 2 Documentation, Release 2.2.5 1 2 3 4 5 use ZendDbSqlSql; $sql = new Sql($adapter); $select = $sql->select(); $select->from(’foo’); $select->where(array(’id’ => 2)); 6 7 8 $selectString = $sql->getSqlStringForSqlObject($select); $results = $adapter->query($selectString, $adapter::QUERY_MODE_EXECUTE); ZendDbSqlSql objects can also be bound to a particular table so that in getting a select, insert, update, or delete object, they are all primarily seeded with the same table when produced. 1 2 3 4 use ZendDbSqlSql; $sql = new Sql($adapter, ’foo’); $select = $sql->select(); $select->where(array(’id’ => 2)); // $select already has the from(’foo’) applied 67.2 ZendDbSql’s Select, Insert, Update and Delete Each of these objects implements the following (2) interfaces: 1 2 3 4 5 6 interface PreparableSqlInterface { public function prepareStatement(Adapter $adapter, StatementInterface $statement); } interface SqlInterface { public function getSqlString(PlatformInterface $adapterPlatform = null); } These are the functions you can call to either produce (a) a prepared statement, or (b) a string to be executed. 67.3 ZendDbSqlSelect ZendDbSqlSelect is an object who’s primary function is to present a unified API for building platform specific SQL SELECT queries. The class can be instantiated and consumed without ZendDbSqlSql: 1 2 3 4 use ZendDbSqlSelect; $select = new Select(); // or, to produce a $select bound to a specific table $select = new Select(’foo’); If a table is provided to the Select object, then from() cannot be called later to change the name of the table. Once you have a valid Select object, the following API can be used to further specify various select statement parts: 1 2 3 4 5 6 7 8 9 class Select extends AbstractSql implements SqlInterface, PreparableSqlInterface { const JOIN_INNER = ’inner’; const JOIN_OUTER = ’outer’; const JOIN_LEFT = ’left’; const JOIN_RIGHT = ’right’; const SQL_STAR = ’*’; const ORDER_ASCENDING = ’ASC’; const ORDER_DESCENDING = ’DESC’; 10 public $where; // @param Where $where 11 310 Chapter 67. ZendDbSql
  • 349. Zend Framework 2 Documentation, Release 2.2.5 12 public public public public public public public public public public 13 14 15 16 17 18 19 20 21 22 23 function function function function function function function function function function __construct($table = null); from($table); columns(array $columns, $prefixColumnsWithTable = true); join($name, $on, $columns = self::SQL_STAR, $type = self::JOIN_INNER); where($predicate, $combination = PredicatePredicateSet::OP_AND); group($group); having($predicate, $combination = PredicatePredicateSet::OP_AND); order($order); limit($limit); offset($offset); } 67.3.1 from(): 1 2 // as a string: $select->from(’foo’); 3 4 5 // as an array to specify an alias: // produces SELECT "t".* FROM "table" AS "t" 6 7 $select->from(array(’t’ => ’table’)); 8 9 10 // using a SqlTableIdentifier: // same output as above 11 12 $select->from(new TableIdentifier(array(’t’ => ’table’))); 67.3.2 columns(): 1 2 // as array of names $select->columns(array(’foo’, ’bar’)); 3 4 5 // as an associative array with aliases as the keys: // produces ’bar’ AS ’foo’, ’bax’ AS ’baz’ 6 7 $select->columns(array(’foo’ => ’bar’, ’baz’ => ’bax’)); 67.3.3 join(): 1 2 3 4 5 6 $select->join( ’foo’, // table name ’id = bar.id’, // expression to join on (will be quoted by platform object before insertion), array(’bar’, ’baz’), // (optional) list of columns, same requirements as columns() above $select::JOIN_OUTER // (optional), one of inner, outer, left, right also represented by constant ); 7 8 9 10 $select->from(array(’f’ => ’foo’)) ->join(array(’b’ => ’bar’), ’f.foo_id = b.foo_id’); 67.3. ZendDbSqlSelect // base table // join table with alias // join expression 311
  • 350. Zend Framework 2 Documentation, Release 2.2.5 67.3.4 where(), having(): The ZendDbSqlSelect object provides bit of flexibility as it regards to what kind of parameters are acceptable when calling where() or having(). The method signature is listed as: 1 2 3 4 5 6 7 8 /** * Create where clause * * @param Where|Closure|string|array $predicate * @param string $combination One of the OP_* constants from PredicatePredicateSet * @return Select */ public function where($predicate, $combination = PredicatePredicateSet::OP_AND); As you can see, there are a number of different ways to pass criteria to both having() and where(). If you provide a ZendDbSqlWhere object to where() or a ZendDbSqlHaving object to having(), the internal objects for Select will be replaced completely. When the where/having() is processed, this object will be iterated to produce the WHERE or HAVING section of the SELECT statement. If you provide a Closure to where() or having(), this function will be called with the Select’s Where object as the only parameter. So the following is possible: 1 2 3 $spec = function (Where $where) { $where->like(’username’, ’ralph%’); }; 4 5 $select->where($spec); If you provide a string, this string will be used to instantiate a ZendDbSqlPredicateExpression object so that it’s contents will be applied as is. This means that there will be no quoting in the fragment provided. Consider the following code: 1 2 // SELECT "foo".* FROM "foo" WHERE x = 5 $select->from(’foo’)->where(’x = 5’); If you provide an array who’s values are keyed by an integer, the value can either be a string that will be then used to build a PredicateExpression or any object that implements PredicatePredicateInterface. These objects are pushed onto the Where stack with the $combination provided. Consider the following code: 1 2 // SELECT "foo".* FROM "foo" WHERE x = 5 AND y = z $select->from(’foo’)->where(array(’x = 5’, ’y = z’)); If you provide an array who’s values are keyed with a string, these values will be handled in the following: • PHP value nulls will be made into a PredicateIsNull object • PHP value array()s will be made into a PredicateIn object • PHP value strings will be made into a PredicateOperator object such that the string key will be identifier, and the value will target value. Consider the following code: 1 2 3 4 // SELECT "foo".* FROM "foo" WHERE "c1" IS NULL AND "c2" IN (?, ?, ?) AND "c3" IS NOT NULL $select->from(’foo’)->where(array( ’c1’ => null, ’c2’ => array(1, 2, 3), 312 Chapter 67. ZendDbSql
  • 351. Zend Framework 2 Documentation, Release 2.2.5 new ZendDbSqlPredicateIsNotNull(’c3’) 5 6 )); 67.3.5 order(): 1 2 $select = new Select; $select->order(’id DESC’); // produces ’id’ DESC 3 4 5 6 $select = new Select; $select->order(’id DESC’) ->order(’name ASC, age DESC’); // produces ’id’ DESC, ’name’ ASC, ’age’ DESC 7 8 9 $select = new Select; $select->order(array(’name ASC’, ’age DESC’)); // produces ’name’ ASC, ’age’ DESC 67.3.6 limit() and offset(): 1 2 3 $select = new Select; $select->limit(5); // always takes an integer/numeric $select->offset(10); // similarly takes an integer/numeric 67.4 ZendDbSqlInsert The Insert API: 1 2 3 4 class Insert implements SqlInterface, PreparableSqlInterface { const VALUES_MERGE = ’merge’; const VALUES_SET = ’set’; 5 public public public public 6 7 8 9 10 function function function function __construct($table = null); into($table); columns(array $columns); values(array $values, $flag = self::VALUES_SET); } Similarly to Select objects, the table can be set at construction time or via into(). 67.4.1 columns(): 1 $insert->columns(array(’foo’, ’bar’)); // set the valid columns 67.4.2 values(): 1 2 3 4 5 6 // default behavior of values is to set the values // successive calls will not preserve values from previous calls $insert->values(array( ’col_1’ => ’value1’, ’col_2’ => ’value2’ )); 67.4. ZendDbSqlInsert 313
  • 352. Zend Framework 2 Documentation, Release 2.2.5 1 2 // merging values with previous calls $insert->values(array(’col_2’ => ’value2’), $insert::VALUES_MERGE); 67.5 ZendDbSqlUpdate 1 2 3 4 class Update { const VALUES_MERGE = ’merge’; const VALUES_SET = ’set’; 5 public public public public public 6 7 8 9 10 11 $where; // @param Where $where function __construct($table = null); function table($table); function set(array $values, $flag = self::VALUES_SET); function where($predicate, $combination = PredicatePredicateSet::OP_AND); } 67.5.1 set(): 1 $update->set(array(’foo’ => ’bar’, ’baz’ => ’bax’)); 67.5.2 where(): See where section below. 67.6 ZendDbSqlDelete 1 2 3 4 5 6 7 class Delete { public $where; // @param Where $where public function __construct($table = null); public function from($table); public function where($predicate, $combination = PredicatePredicateSet::OP_AND); } 67.6.1 where(): See where section below. 67.7 ZendDbSqlWhere & ZendDbSqlHaving In the following, we will talk about Where, Having is implies as being the same API. Effectively, Where and Having extend from the same base object, a Predicate (and PredicateSet). All of the parts that make up a where or having that are and’ed or or’d together are called predicates. The full set of predicates is called a PredicateSet. This object set generally contains the values (and identifiers) separate from the fragment they belong to until the last possible moment when the statement is either used to be prepared (parameteritized), or executed. In 314 Chapter 67. ZendDbSql
  • 353. Zend Framework 2 Documentation, Release 2.2.5 parameterization, the parameters will be replaced with their proper placeholder (a named or positional parameter), and the values stored inside a AdapterParameterContainer. When executed, the values will be interpolated into the fragments they belong to and properly quoted. It is important to know that in this API, a distinction is made between what elements are considered identifiers (TYPE_IDENTIFIER) and which of those is a value (TYPE_VALUE). There is also a special use case type for literal values (TYPE_LITERAL). These are all exposed via the ZendDbSqlExpressionInterface interface. Note: In ZF 2.1, an actual Literal type was added. ZendDbSql now makes the distinction that Literals will not have any parameters that need interpolating whereas it is expected that Expression objects might have parameters that need interpolating. In cases where there are parameters in an Expression, ZendDbSqlAbstractSql will do its best to identify placeholders when the Expression is processed during statement creation. In short, if you don’t have parameters, use Literal objects. The ZendDbSqlWhere (Predicate/PredicateSet) API: 1 2 3 4 5 6 7 8 9 // Where & Having: class Predicate extends PredicateSet { public $and; public $or; public $AND; public $OR; public $NEST; public $UNNEST; 10 public public public public public public public public public public public public public public public 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 function function function function function function function function function function function function function function function nest(); setUnnest(Predicate $predicate); unnest(); equalTo($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightType = self::TYP lessThan($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightType = self::TY greaterThan($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightType = self: lessThanOrEqualTo($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightType = greaterThanOrEqualTo($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightTyp like($identifier, $like); literal($literal); expression($expression, $parameter); isNull($identifier); isNotNull($identifier); in($identifier, array $valueSet = array()); between($identifier, $minValue, $maxValue); 26 27 // Inherited From PredicateSet 28 29 public public public public public public 30 31 32 33 34 35 36 function function function function function function addPredicate(PredicateInterface $predicate, $combination = null); getPredicates(); orPredicate(PredicateInterface $predicate); andPredicate(PredicateInterface $predicate); getExpressionData(); count(); } Each method in the Where API will produce a corresponding Predicate object of a similarly named type, described below, with the full API of the object: 67.7. ZendDbSqlWhere & ZendDbSqlHaving 315
  • 354. Zend Framework 2 Documentation, Release 2.2.5 67.7.1 equalTo(), lessThan(), greaterThan(), lessThanOrEqualTo(), greaterThanOrEqualTo(): 1 $where->equalTo(’id’, 5); 2 3 4 5 6 // same as the following workflow $where->addPredicate( new PredicateOperator($left, Operator::OPERATOR_EQUAL_TO, $right, $leftType, $rightType) ); 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Operator implements PredicateInterface { const OPERATOR_EQUAL_TO const OP_EQ const OPERATOR_NOT_EQUAL_TO const OP_NE const OPERATOR_LESS_THAN const OP_LT const OPERATOR_LESS_THAN_OR_EQUAL_TO const OP_LTE const OPERATOR_GREATER_THAN const OP_GT const OPERATOR_GREATER_THAN_OR_EQUAL_TO const OP_GTE = = = = = = = = = = = = ’=’; ’=’; ’!=’; ’!=’; ’<’; ’<’; ’<=’; ’<=’; ’>’; ’>’; ’>=’; ’>=’; 22 public public public public public public public public public public public public 23 24 25 26 27 28 29 30 31 32 33 34 35 function function function function function function function function function function function function __construct($left = null, $operator = self::OPERATOR_EQUAL_TO, $right = null, $le setLeft($left); getLeft(); setLeftType($type); getLeftType(); setOperator($operator); getOperator(); setRight($value); getRight(); setRightType($type); getRightType(); getExpressionData(); } 67.7.2 like($identifier, $like): 1 $where->like($identifier, $like): 2 3 4 5 6 // same as $where->addPredicate( new PredicateLike($identifier, $like) ); 7 8 // full API 9 10 11 12 13 14 15 class Like { public public public public 316 implements PredicateInterface function function function function __construct($identifier = null, $like = null); setIdentifier($identifier); getIdentifier(); setLike($like); Chapter 67. ZendDbSql
  • 355. Zend Framework 2 Documentation, Release 2.2.5 public function getLike(); 16 17 } 67.7.3 literal($literal); 1 $where->literal($literal); 2 3 4 5 6 // same as $where->addPredicate( new PredicateLiteral($literal) ); 7 8 9 10 11 12 13 14 15 // full API class Literal implements ExpressionInterface, PredicateInterface { const PLACEHOLDER = ’?’; public function __construct($literal = ’’); public function setLiteral($literal); public function getLiteral(); } 67.7.4 expression($expression, $parameter); 1 $where->expression($expression, $parameter); 2 3 4 5 6 // same as $where->addPredicate( new PredicateExpression($expression, $parameter) ); 7 8 9 10 11 12 13 14 15 16 17 18 19 // full API class Expression implements ExpressionInterface, PredicateInterface { const PLACEHOLDER = ’?’; public function __construct($expression = null, $valueParameter = null /*[, $valueParameter, ... public function setExpression($expression); public function getExpression(); public function setParameters($parameters); public function getParameters(); public function setTypes(array $types); public function getTypes(); } 67.7.5 isNull($identifier); 1 $where->isNull($identifier); 2 3 4 5 6 // same as $where->addPredicate( new PredicateIsNull($identifier) ); 7 8 // full API 67.7. ZendDbSqlWhere & ZendDbSqlHaving 317
  • 356. Zend Framework 2 Documentation, Release 2.2.5 9 10 11 12 13 14 class IsNull implements PredicateInterface { public function __construct($identifier = null); public function setIdentifier($identifier); public function getIdentifier(); } 67.7.6 isNotNull($identifier); 1 $where->isNotNull($identifier); 2 3 4 5 6 // same as $where->addPredicate( new PredicateIsNotNull($identifier) ); 7 8 9 10 11 12 13 14 // full API class IsNotNull implements PredicateInterface { public function __construct($identifier = null); public function setIdentifier($identifier); public function getIdentifier(); } 67.7.7 in($identifier, array $valueSet = array()); 1 $where->in($identifier, array $valueSet = array()); 2 3 4 5 6 // same as $where->addPredicate( new PredicateIn($identifier, $valueSet) ); 7 8 9 10 11 12 13 14 15 16 // full API class In implements { public function public function public function public function public function } PredicateInterface __construct($identifier = null, array $valueSet = array()); setIdentifier($identifier); getIdentifier(); setValueSet(array $valueSet); getValueSet(); 67.7.8 between($identifier, $minValue, $maxValue); 1 $where->between($identifier, $minValue, $maxValue); 2 3 4 5 6 // same as $where->addPredicate( new PredicateBetween($identifier, $minValue, $maxValue) ); 7 8 // full API 318 Chapter 67. ZendDbSql
  • 357. Zend Framework 2 Documentation, Release 2.2.5 9 10 11 12 13 14 15 16 17 18 19 class Between implements PredicateInterface { public function __construct($identifier = null, $minValue = null, $maxValue = null); public function setIdentifier($identifier); public function getIdentifier(); public function setMinValue($minValue); public function getMinValue(); public function setMaxValue($maxValue); public function getMaxValue(); public function setSpecification($specification); } 67.7. ZendDbSqlWhere & ZendDbSqlHaving 319
  • 358. Zend Framework 2 Documentation, Release 2.2.5 320 Chapter 67. ZendDbSql
  • 359. CHAPTER 68 ZendDbSqlDdl ZendDbSqlDdl is a sub-component of ZendDbSql that allows consumers to create statement objects that will produce DDL (Data Definition Language) SQL statements. When combined with a platform specific ZendDbSqlSql object, these DDL objects are capable of producing platform-specific CREATE TABLE statements, with specialized data types, constraints, and indexes for a database/schema. The following platforms have platform specializations for DDL: • MySQL • All databases compatible with ANSI SQL92 321
  • 360. Zend Framework 2 Documentation, Release 2.2.5 322 Chapter 68. ZendDbSqlDdl
  • 361. CHAPTER 69 Creating Tables Like ZendDbSql objects, each statement type is represented by a class. For example, CREATE TABLE is modeled by a CreateTable object; this is likewise the same for ALTER TABLE (as AlterTable), and DROP TABLE (as DropTable). These classes exist in the ZendDbSqlDdl namespace. To initiate the building of a DDL statement, such as CreateTable, one needs to instantiate the object. There are a couple of valid patterns for this: 1 use ZendDbSqlDdl; 2 3 $table = new DdlCreateTable(); 4 5 6 // or with table $table = new DdlCreateTable(’bar’); 7 8 9 // optionally, as a temporary table $table = new DdlCreateTable(’bar’, true); You can also set the table after instantiation: 1 $table->setTable(’bar’); Currently, columns are added by creating a column object, described in the data type table in the data type section below: 1 2 3 use ZendDbSqlDdlColumn; $table->addColumn(new ColumnInteger(’id’)); $table->addColumn(new ColumnVarchar(’name’, 255)); Beyond adding columns to a table, constraints can also be added: 1 2 3 4 5 use ZendDbSqlDdlConstraint; $table->addConstraint(new ConstraintPrimaryKey(’id’)); $table->addConstraint( new ConstraintUniqueKey([’name’, ’foo’], ’my_unique_key’) ); 323
  • 362. Zend Framework 2 Documentation, Release 2.2.5 324 Chapter 69. Creating Tables
  • 363. CHAPTER 70 Altering Tables Similarly to CreateTable, you may also instantiate AlterTable: 1 use ZendDbSqlDdl; 2 3 $table = new DdlAlterTable(); 4 5 6 // or with table $table = new DdlAlterTable(’bar’); 7 8 9 // optionally, as a temporary table $table = new DdlAlterTable(’bar’, true); The primary difference between a CreateTable and AlterTable is that the AlterTable takes into account that the table and its assets already exist. Therefore, while you still have addColumn() and addConstraint(), you will also see the ability to change existing columns: 1 2 use ZendDbSqlDdlColumn; $table->changeColumn(’name’, ColumnVarchar(’new_name’, 50)); You may also drop existing columns or constraints: 1 2 $table->dropColumn(’foo’); $table->dropConstraint(’my_index’); 325
  • 364. Zend Framework 2 Documentation, Release 2.2.5 326 Chapter 70. Altering Tables
  • 365. CHAPTER 71 Dropping Tables To drop a table, create a DropTable statement object: 1 $drop = new DdlDropTable(’bar’); 327
  • 366. Zend Framework 2 Documentation, Release 2.2.5 328 Chapter 71. Dropping Tables
  • 367. CHAPTER 72 Executing DDL Statements After a DDL statement object has been created and configured, at some point you will want to execute the statement. To do this, you will need two other objects: an Adapter instance, and a properly seeded Sql instance. The workflow looks something like this, with $ddl being a CreateTable, AlterTable, or DropTable instance: 1 use ZendDbSqlSql; 2 3 4 // existence of $adapter is assumed $sql = new Sql($adapter); 5 6 7 8 9 $adapter->query( $sql->getSqlStringForSqlObject($ddl), $adapter::QUERY_MODE_EXECUTE ); By passing the $ddl object through the $sql object’s getSqlStringForSqlObject() method, we ensure that any platform specific specializations/modifications are utilized to create a platform specific SQL statement. Next, using the constant ZendDbAdapterAdapter::QUERY_MODE_EXECUTE ensures that the SQL statement is not prepared, as many DDL statements on a variety of platforms cannot be prepared, only executed. 329
  • 368. Zend Framework 2 Documentation, Release 2.2.5 330 Chapter 72. Executing DDL Statements
  • 369. CHAPTER 73 Currently Supported Data Types These types exist in the ZendDbSqlDdlColumn namespace. ZendDbSqlDdlColumnColumnInterface. Data types must implement In alphabetical order: Type Blob Boolean Char Column (generic) Date Decimal Float Integer Time Varchar Arguments For Construction $name, $length, $nullable = false, $default = null, array $options = array() $name $name, $length $name = null $name $name, $precision, $scale = null $name, $digits, $decimal $name, $nullable = false, $default = null, array $options = array() $name $name, $length Each of the above types can be utilized in any place that accepts a ColumnColumnInterface instance. Currently, this is primarily in CreateTable::addColumn() and AlterTable‘s addColumn() and changeColumn() methods. 331
  • 370. Zend Framework 2 Documentation, Release 2.2.5 332 Chapter 73. Currently Supported Data Types
  • 371. CHAPTER 74 Currently Supported Constraint Types These types exist in the ZendDbSqlDdlConstraint namespace. ZendDbSqlDdlConstraintConstraintInterface. Data types must implement In alphabetical order: Type Arguments For Construction Check $expression, $name For$name, $column, $referenceTable, $referenceColumn, $onDeleteRule = eignKey null, $onUpdateRule = null Prima$columns ryKey UniqueKey $column, $name = null Each of the above types can be utilized in any place that accepts a ColumnConstraintInterface instance. Currently, this is primarily in CreateTable::addConstraint() and AlterTable::addConstraint(). 333
  • 372. Zend Framework 2 Documentation, Release 2.2.5 334 Chapter 74. Currently Supported Constraint Types
  • 373. CHAPTER 75 ZendDbTableGateway The Table Gateway object is intended to provide an object that represents a table in a database, and the methods of this object mirror the most common operations on a database table. In code, the interface for such an object looks like this: 1 2 3 4 5 6 7 8 interface ZendDbTableGatewayTableGatewayInterface { public function getTable(); public function select($where = null); public function insert($set); public function update($set, $where = null); public function delete($where); } There are two primary implementations of the TableGatewayInterface that are of the most useful: AbstractTableGateway and TableGateway. The AbstractTableGateway is an abstract basic implementation that provides functionality for select(), insert(), update(), delete(), as well as an additional API for doing these same kinds of tasks with explicit SQL objects. These methods are selectWith(), insertWith(), updateWith() and deleteWith(). In addition, AbstractTableGateway also implements a “Feature” API, that allows for expanding the behaviors of the base TableGateway implementation without having to extend the class with this new functionality. The TableGateway concrete implementation simply adds a sensible constructor to the AbstractTableGateway class so that out-of-the-box, TableGateway does not need to be extended in order to be consumed and utilized to its fullest. 75.1 Basic Usage The quickest way to get up and running with ZendDbTableGateway is to configure and utilize the concrete implementation of the TableGateway. The API of the concrete TableGateway is: 1 2 3 4 5 class TableGateway extends AbstractTableGateway { public $lastInsertValue; public $table; public $adapter; 6 7 public function __construct($table, Adapter $adapter, $features = null, ResultSet $resultSetProt 8 9 /** Inherited from AbstractTableGateway */ 335
  • 374. Zend Framework 2 Documentation, Release 2.2.5 10 public public public public public public public public public public public public public public public public public 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function function function function function function function function function function function function function function function function function isInitialized(); initialize(); getTable(); getAdapter(); getColumns(); getFeatureSet(); getResultSetPrototype(); getSql(); select($where = null); selectWith(Select $select); insert($set); insertWith(Insert $insert); update($set, $where = null); updateWith(Update $update); delete($where); deleteWith(Delete $delete); getLastInsertValue(); } The concrete TableGateway object practices constructor injection for getting dependencies and options into the instance. The table name and an instance of an Adapter are all that is needed to setup a working TableGateway object. Out of the box, this implementation makes no assumptions about table structure or metadata, and when select() is executed, a simple ResultSet object with the populated Adapter’s Result (the datasource) will be returned and ready for iteration. 1 2 3 use ZendDbTableGatewayTableGateway; $projectTable = new TableGateway(’project’, $adapter); $rowset = $projectTable->select(array(’type’ => ’PHP’)); 4 5 6 7 8 echo ’Projects of type PHP: ’; foreach ($rowset as $projectRow) { echo $projectRow[’name’] . PHP_EOL; } 9 10 11 12 13 // or, when expecting a single row: $artistTable = new TableGateway(’artist’, $adapter); $rowset = $artistTable->select(array(’id’ => 2)); $artistRow = $rowset->current(); 14 15 var_dump($artistRow); The select() method takes the same arguments as ZendDbSqlSelect::where() with the addition of also being able to accept a closure, which in turn, will be passed the current Select object that is being used to build the SELECT query. The following usage is possible: 1 2 3 use ZendDbTableGatewayTableGateway; use ZendDbSqlSelect; $artistTable = new TableGateway(’artist’, $adapter); 4 5 6 7 8 9 // search for at most 2 artists who’s name starts with Brit, ascending $rowset = $artistTable->select(function (Select $select) { $select->where->like(’name’, ’Brit%’); $select->order(’name ASC’)->limit(2); }); 336 Chapter 75. ZendDbTableGateway
  • 375. Zend Framework 2 Documentation, Release 2.2.5 75.2 TableGateway Features The Features API allows for extending the functionality of the base TableGateway object without having to polymorphically extend the base class. This allows for a wider array of possible mixing and matching of features to achieve a particular behavior that needs to be attained to make the base implementation of TableGateway useful for a particular problem. With the TableGateway object, features should be injected though the constructor. The constructor can take Features in 3 different forms: as a single feature object, as a FeatureSet object, or as an array of Feature objects. There are a number of features built-in and shipped with ZendDb: • GlobalAdapterFeature: the ability to use a global/static adapter without needing to inject it into a TableGateway instance. This is more useful when you are extending the AbstractTableGateway implementation: 1 2 use ZendDbTableGatewayAbstractTableGateway; use ZendDbTableGatewayFeature; 3 4 5 6 7 8 9 10 11 12 13 class MyTableGateway extends AbstractTableGateway { public function __construct() { $this->table = ’my_table’; $this->featureSet = new FeatureFeatureSet(); $this->featureSet->addFeature(new FeatureGlobalAdapterFeature()); $this->initialize(); } } 14 15 16 // elsewhere in code, in a bootstrap ZendDbTableGatewayFeatureGlobalAdapterFeature::setStaticAdapter($adapter); 17 18 19 // in a controller, or model somewhere $table = new MyTableGateway(); // adapter is statically loaded • MasterSlaveFeature: the ability to use a master adapter for insert(), update(), and delete() while using a slave adapter for all select() operations. 1 $table = new TableGateway(’artist’, $adapter, new FeatureMasterSlaveFeature($slaveAdapter)); • MetadataFeature: the ability populate TableGateway with column information from a Metadata object. It will also store the primary key information in case RowGatewayFeature needs to consume this information. 1 $table = new TableGateway(’artist’, $adapter, new FeatureMetadataFeature()); • EventFeature: the ability utilize a TableGateway object with ZendEventManager and to be able to subscribe to various events in a TableGateway lifecycle. 1 $table = new TableGateway(’artist’, $adapter, new FeatureEventFeature($eventManagerInstance)); • RowGatewayFeature: the ability for select() to return a ResultSet object that upon iteration will return a RowGateway object for each row. 1 2 $table = new TableGateway(’artist’, $adapter, new FeatureRowGatewayFeature(’id’)); $results = $table->select(array(’id’ => 2)); 3 4 $artistRow = $results->current(); 75.2. TableGateway Features 337
  • 376. Zend Framework 2 Documentation, Release 2.2.5 5 6 $artistRow->name = ’New Name’; $artistRow->save(); 338 Chapter 75. ZendDbTableGateway
  • 377. CHAPTER 76 ZendDbRowGateway ZendDbRowGateway is a sub-component of ZendDb that implements the Row Gateway pattern from PoEAA. This effectively means that Row Gateway objects primarily model a row in a database, and have methods such as save() and delete() that will help persist this row-as-an-object in the database itself. Likewise, after a row from the database is retrieved, it can then be manipulated and save()’d back to the database in the same position (row), or it can be delete()’d from the table. The interface for a Row Gateway object simply adds save() and delete() and this is the interface that should be assumed when a component has a dependency that is expected to be an instance of a RowGateway object: 1 2 3 4 5 interface RowGatewayInterface { public function save(); public function delete(); } 76.1 Quickstart While most of the time, RowGateway will be used in conjunction with other ZendDbResultSet producing objects, it is possible to use it standalone. To use it standalone, you simply need an Adapter and a set of data to work with. The following use case demonstrates ZendDbRowGatewayRowGateway usage in its simplest form: 1 use ZendDbRowGatewayRowGateway; 2 3 4 // query the database $resultSet = $adapter->query(’SELECT * FROM ‘user‘ WHERE ‘id‘ = ?’, array(2)); 5 6 7 // get array of data $rowData = $resultSet->current()->getArrayCopy(); 8 9 10 11 // row gateway $rowGateway = new RowGateway(’id’, ’my_table’, $adapter); $rowGateway->populate($rowData); 12 13 14 $rowGateway->first_name = ’New Name’; $rowGateway->save(); 15 339
  • 378. Zend Framework 2 Documentation, Release 2.2.5 16 17 // or delete this row: $rowGateway->delete(); The workflow described above is greatly simplified when RowGateway is used in conjunction with the TableGateway feature. What this achieves is a Table Gateway object that when select()’ing from a table, will produce a ResultSet that is then capable of producing valid Row Gateway objects. Its usage looks like this: 1 2 use ZendDbTableGatewayFeatureRowGatewayFeature; use ZendDbTableGatewayTableGateway; 3 4 5 $table = new TableGateway(’artist’, $adapter, new RowGatewayFeature(’id’)); $results = $table->select(array(’id’ => 2)); 6 7 8 9 $artistRow = $results->current(); $artistRow->name = ’New Name’; $artistRow->save(); 76.2 ActiveRecord Style Objects If you wish to have custom behaviour for your RowGateway objects (essentially making them behave similarly to the ActiveRecord pattern), pass a prototype object implementing the RowGatewayInterface to the RowGatewayFeature constructor instead of a primary key: 1 2 3 use ZendDbTableGatewayFeatureRowGatewayFeature; use ZendDbTableGatewayTableGateway; use ZendDbRowGatewayRowGatewayInterface; 4 5 6 7 class Artist implements RowGatewayInterface { protected $adapter; 8 public function __construct($adapter) { $this->adapter = $adapter; } 9 10 11 12 13 // ... save() and delete() implementations 14 15 } 16 17 $table = new TableGateway(’artist’, $adapter, new RowGatewayFeature(new Artist($adapter))); 340 Chapter 76. ZendDbRowGateway
  • 379. CHAPTER 77 ZendDbMetadata ZendDbMetadata is as sub-component of ZendDb that makes it possible to get metadata information about tables, columns, constraints, triggers, and other information from a database in a standardized way. The primary interface for the Metadata objects is: 1 2 3 interface MetadataInterface { public function getSchemas(); 4 public function getTableNames($schema = null, $includeViews = false); public function getTables($schema = null, $includeViews = false); public function getTable($tableName, $schema = null); 5 6 7 8 public function getViewNames($schema = null); public function getViews($schema = null); public function getView($viewName, $schema = null); 9 10 11 12 public function getColumnNames($table, $schema = null); public function getColumns($table, $schema = null); public function getColumn($columnName, $table, $schema = null); 13 14 15 16 public function getConstraints($table, $schema = null); public function getConstraint($constraintName, $table, $schema = null); public function getConstraintKeys($constraint, $table, $schema = null); 17 18 19 20 public function getTriggerNames($schema = null); public function getTriggers($schema = null); public function getTrigger($triggerName, $schema = null); 21 22 23 24 } 77.1 Basic Usage Usage of ZendDbMetadata is very straight forward. The top level class ZendDbMetadataMetadata will, given an adapter, choose the best strategy (based on the database platform being used) for retrieving metadata. In most cases, information will come from querying the INFORMATION_SCHEMA tables generally accessible to all database connections about the currently accessible schema. 341
  • 380. Zend Framework 2 Documentation, Release 2.2.5 Metadata::get*Names() methods will return an array of strings, while the other methods will return specific value objects with the containing information. This is best demonstrated by the script below. 1 $metadata = new ZendDbMetadataMetadata($adapter); 2 3 4 // get the table names $tableNames = $metadata->getTableNames(); 5 6 7 foreach ($tableNames as $tableName) { echo ’In Table ’ . $tableName . PHP_EOL; 8 $table = $metadata->getTable($tableName); 9 10 11 echo ’ With columns: ’ . PHP_EOL; foreach ($table->getColumns() as $column) { echo ’ ’ . $column->getName() . ’ -> ’ . $column->getDataType() . PHP_EOL; } 12 13 14 15 16 17 18 echo PHP_EOL; echo ’ With constraints: ’ . PHP_EOL; 19 20 21 foreach ($metadata->getConstraints($tableName) as $constraint) { /** @var $constraint ZendDbMetadataObjectConstraintObject */ echo ’ ’ . $constraint->getName() . ’ -> ’ . $constraint->getType() . PHP_EOL; if (!$constraint->hasColumns()) { continue; } echo ’ column: ’ . implode(’, ’, $constraint->getColumns()); if ($constraint->isForeignKey()) { $fkCols = array(); foreach ($constraint->getReferencedColumns() as $refColumn) { $fkCols[] = $constraint->getReferencedTableName() . ’.’ . $refColumn; } echo ’ => ’ . implode(’, ’, $fkCols); } echo PHP_EOL; 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 } 40 41 echo ’----’ . PHP_EOL; 42 43 } Metadata returns value objects that provide an interface to help developers better explore the metadata. Below is the API for the various value objects: The TableObject: 1 2 3 4 5 6 7 class ZendDbMetadataObjectTableObject { public function __construct($name); public function setColumns(array $columns); public function getColumns(); public function setConstraints($constraints); public function getConstraints(); 342 Chapter 77. ZendDbMetadata
  • 381. Zend Framework 2 Documentation, Release 2.2.5 public function setName($name); public function getName(); 8 9 10 } The ColumnObject: 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 class ZendDbMetadataObjectColumnObject { public function __construct($name, $tableName, $schemaName = null); public function setName($name); public function getName(); public function getTableName(); public function setTableName($tableName); public function setSchemaName($schemaName); public function getSchemaName(); public function getOrdinalPosition(); public function setOrdinalPosition($ordinalPosition); public function getColumnDefault(); public function setColumnDefault($columnDefault); public function getIsNullable(); public function setIsNullable($isNullable); public function isNullable(); public function getDataType(); public function setDataType($dataType); public function getCharacterMaximumLength(); public function setCharacterMaximumLength($characterMaximumLength); public function getCharacterOctetLength(); public function setCharacterOctetLength($characterOctetLength); public function getNumericPrecision(); public function setNumericPrecision($numericPrecision); public function getNumericScale(); public function setNumericScale($numericScale); public function getNumericUnsigned(); public function setNumericUnsigned($numericUnsigned); public function isNumericUnsigned(); public function getErratas(); public function setErratas(array $erratas); public function getErrata($errataName); public function setErrata($errataName, $errataValue); } The ConstraintObject: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class ZendDbMetadataObjectConstraintObject { public function __construct($name, $tableName, $schemaName = null); public function setName($name); public function getName(); public function setSchemaName($schemaName); public function getSchemaName(); public function getTableName(); public function setTableName($tableName); public function setType($type); public function getType(); public function hasColumns(); public function getColumns(); public function setColumns(array $columns); public function getReferencedTableSchema(); public function setReferencedTableSchema($referencedTableSchema); public function getReferencedTableName(); 77.1. Basic Usage 343
  • 382. Zend Framework 2 Documentation, Release 2.2.5 public public public public public public public public public public public public public public public 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 function function function function function function function function function function function function function function function setReferencedTableName($referencedTableName); getReferencedColumns(); setReferencedColumns(array $referencedColumns); getMatchOption(); setMatchOption($matchOption); getUpdateRule(); setUpdateRule($updateRule); getDeleteRule(); setDeleteRule($deleteRule); getCheckClause(); setCheckClause($checkClause); isPrimaryKey(); isUnique(); isForeignKey(); isCheck(); 33 34 } The TriggerObject: 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 class ZendDbMetadataObjectTriggerObject { public function getName(); public function setName($name); public function getEventManipulation(); public function setEventManipulation($eventManipulation); public function getEventObjectCatalog(); public function setEventObjectCatalog($eventObjectCatalog); public function getEventObjectSchema(); public function setEventObjectSchema($eventObjectSchema); public function getEventObjectTable(); public function setEventObjectTable($eventObjectTable); public function getActionOrder(); public function setActionOrder($actionOrder); public function getActionCondition(); public function setActionCondition($actionCondition); public function getActionStatement(); public function setActionStatement($actionStatement); public function getActionOrientation(); public function setActionOrientation($actionOrientation); public function getActionTiming(); public function setActionTiming($actionTiming); public function getActionReferenceOldTable(); public function setActionReferenceOldTable($actionReferenceOldTable); public function getActionReferenceNewTable(); public function setActionReferenceNewTable($actionReferenceNewTable); public function getActionReferenceOldRow(); public function setActionReferenceOldRow($actionReferenceOldRow); public function getActionReferenceNewRow(); public function setActionReferenceNewRow($actionReferenceNewRow); public function getCreated(); public function setCreated($created); } 344 Chapter 77. ZendDbMetadata
  • 383. CHAPTER 78 Introduction to ZendDi 78.1 Dependency Injection Dependency Injection (here-in called DI) is a concept that has been talked about in numerous places over the web. Simply put, we’ll explain the act of injecting dependencies simply with this below code: 1 $b = new MovieLister(new MovieFinder()); Above, MovieFinder is a dependency of MovieLister, and MovieFinder was injected into MovieLister. If you are not familiar with the concept of DI, here are a couple of great reads: Matthew Weier O’Phinney’s Analogy, Ralph Schindler’s Learning DI, or Fabien Potencier’s Series on DI. 78.2 Dependency Injection Containers When your code is written in such a way that all your dependencies are injected into consuming objects, you might find that the simple act of wiring an object has gotten more complex. When this becomes the case, and you find that this wiring is creating more boilerplate code, this makes for an excellent opportunity to utilize a Dependency Injection Container. In it’s simplest form, a Dependency Injection Container (here-in called a DiC for brevity), is an object that is capable of creating objects on request and managing the “wiring”, or the injection of required dependencies, for those requested objects. Since the patterns that developers employ in writing DI capable code vary, DiC’s are generally either in the form of smallish objects that suit a very specific pattern, or larger DiC frameworks. ZendDi is a DiC framework. While for the simplest code there is no configuration needed, and the use cases are quite simple; for more complex code, ZendDi is capable of being configured to wire these complex use cases 345
  • 384. Zend Framework 2 Documentation, Release 2.2.5 346 Chapter 78. Introduction to ZendDi
  • 385. CHAPTER 79 ZendDi Quickstart This QuickStart is intended to get developers familiar with the concepts of the ZendDi DiC. Generally speaking, code is never as simple as it is inside this example, so working knowledge of the other sections of the manual is suggested. Assume for a moment, you have the following code as part of your application that you feel is a good candidate for being managed by a DiC, after all, you are already injecting all your dependencies: 1 2 3 4 5 6 7 8 9 10 11 12 13 namespace MyLibrary { class DbAdapter { protected $username = null; protected $password = null; public function __construct($username, $password) { $this->username = $username; $this->password = $password; } } } 14 15 16 17 18 19 20 21 22 23 24 namespace MyMovieApp { class MovieFinder { protected $dbAdapter = null; public function __construct(MyLibraryDbAdapter $dbAdapter) { $this->dbAdapter = $dbAdapter; } } 25 class MovieLister { protected $movieFinder = null; public function __construct(MovieFinder $movieFinder) { $this->movieFinder = $movieFinder; } } 26 27 28 29 30 31 32 33 34 } 347
  • 386. Zend Framework 2 Documentation, Release 2.2.5 With the above code, you find yourself writing the following to wire and utilize this code: 1 // $config object is assumed 2 3 4 5 6 7 8 $dbAdapter = new MyLibraryDbAdapter($config->username, $config->password); $movieFinder = new MyMovieAppMovieFinder($dbAdapter); $movieLister = new MyMovieAppMovieLister($movieFinder); foreach ($movieLister as $movie) { // iterate and display $movie } If you are doing this above wiring in each controller or view that wants to list movies, not only can this become repetitive and boring to write, but also unmaintainable if for example you want to swap out one of these dependencies on a wholesale scale. Since this example of code already practices good dependency injection, with constructor injection, it is a great candidate for using ZendDi. The usage is as simple as: // inside a bootstrap somewhere $di = new ZendDiDi(); $di->instanceManager()->setParameters(’MyLibraryDbAdapter’, array( ’username’ => $config->username, ’password’ => $config->password )); 1 2 3 4 5 6 7 // inside each controller $movieLister = $di->get(’MyMovieAppMovieLister’); foreach ($movieLister as $movie) { // iterate and display $movie } 8 9 10 11 12 In the above example, we are obtaining a default instance of ZendDiDi. By ‘default’, we mean that ZendDiDi is constructed with a DefinitionList seeded with a RuntimeDefinition (uses Reflection) and an empty instance manager and no configuration. Here is the ZendDiDi constructor: public function __construct(DefinitionList $definitions = null, InstanceManager $instanceManager { $this->definitions = ($definitions) ?: new DefinitionList(new DefinitionRuntimeDefinition()) $this->instanceManager = ($instanceManager) ?: new InstanceManager(); 1 2 3 4 5 if ($config) { $this->configure($config); } 6 7 8 } 9 This means that when $di->get() is called, it will be consulting the RuntimeDefinition, which uses reflection to understand the structure of the code. Once it knows the structure of the code, it can then know how the dependencies fit together and how to go about wiring your objects for you. ZendDiDefinitionRuntimeDefinition will utilize the names of the parameters in the methods as the class parameter names. This is how both username and password key are mapped to the first and second parameter, respectively, of the constructor consuming these named parameters. If you were to want to pass in the username and password at call time, this is achieved by passing them as the second argument of get(): // inside each controller $di = new ZendDiDi(); $movieLister = $di->get(’MyMovieAppMovieLister’, array( ’username’ => $config->username, ’password’ => $config->password )); 1 2 3 4 5 6 348 Chapter 79. ZendDi Quickstart
  • 387. Zend Framework 2 Documentation, Release 2.2.5 7 8 9 foreach ($movieLister as $movie) { // iterate and display $movie } It is important to note that when using call time parameters, these parameter names will be applied to any class that accepts a parameter of such name. By calling $di->get(), this instance of MovieLister will be automatically shared. This means subsequent calls to get() will return the same instance as previous calls. If you wish to have completely new instances of MovieLister, you can utilize $di->newInstance(). 349
  • 388. Zend Framework 2 Documentation, Release 2.2.5 350 Chapter 79. ZendDi Quickstart
  • 389. CHAPTER 80 ZendDi Definition Definitions are the place where ZendDi attempts to understand the structure of the code it is attempting to wire. This means that if you’ve written non-ambiguous, clear and concise code; ZendDi has a very good chance of understanding how to wire things up without much added complexity. 80.1 DefinitionList Definitions are introduced to the ZendDiDi object through a definition list implemented as ZendDiDefinitionList (SplDoublyLinkedList). Order is important. Definitions in the front of the list will be consulted on a class before definitions at the end of the list. Note: Regardless of what kind of Definition strategy you decide to use, it is important that your autoloaders are already setup and ready to use. 80.2 RuntimeDefinition The default DefinitionList instantiated by ZendDiDi, when no other DefinitionList is provided, has as DefinitionRuntimeDefinition baked-in. The RuntimeDefinition will respond to query’s about classes by using Reflection. This Runtime definitions uses any available information inside methods: their signature, the names of parameters, the type-hints of the parameters, and the default values to determine if something is optional or required when making a call to that method. The more explicit you can be in your method naming and method signatures, the easier of a time ZendDiDefinitionRuntimeDefinition will have determining the structure of your code. This is what the constructor of a RuntimeDefinition looks like: 1 2 3 4 5 6 7 public function __construct(IntrospectionStrategy $introspectionStrategy = null, array $explicitClass { $this->introspectionStrategy = ($introspectionStrategy) ?: new IntrospectionStrategy(); if ($explicitClasses) { $this->setExplicitClasses($explicitClasses); } } The IntrospectionStrategy object is an object that determines the rules, or guidelines, for how the RuntimeDefinition will introspect information about your classes. Here are the things it knows how to do: 351
  • 390. Zend Framework 2 Documentation, Release 2.2.5 • Whether or not to use Annotations (Annotations are expensive and off by default, read more about these in the Annotations section) • Which method names to include in the introspection, by default, the pattern /^set[A-Z]{1}w*/ is registered by default, this is a list of patterns. • Which interface names represent the interface injection pattern. By default, the pattern /w*Awarew*/ is registered, this is a list of patterns. The constructor for the IntrospectionStrategy looks like this: 1 2 3 4 public function __construct(AnnotationManager $annotationManager = null) { $this->annotationManager = ($annotationManager) ?: $this->createDefaultAnnotationManager(); } This goes to say that an AnnotationManager is not required, but if you wish to create a special AnnotationManager with your own annotations, and also wish to extend the RuntimeDefinition to look for these special Annotations, this is the place to do it. The RuntimeDefinition also can be used to look up either all classes (implicitly, which is default), or explicitly look up for particular pre-defined classes. This is useful when your strategy for inspecting one set of classes might differ from those of another strategy for another set of classes. This can be achieved by using the setExplicitClasses() method or by passing a list of classes as a second argument to the constructor of the RuntimeDefinition. 80.3 CompilerDefinition The CompilerDefinition is very much similar in nature to the RuntimeDefinition with the exception that it can be seeded with more information for the purposes of “compiling” a definition. This is useful when you do not want to be making all those (sometimes expensive) calls to reflection and the annotation scanning system during the request of your application. By using the compiler, a definition can be created and written to disk to be used during a request, as opposed to the task of scanning the actual code. For example, let’s assume we want to create a script that will create definitions for some of our library code: 1 2 3 4 5 // in "package name" format $components = array( ’My_MovieApp’, ’My_OtherClasses’, ); 6 7 8 9 foreach ($components as $component) { $diCompiler = new ZendDiDefinitionCompilerDefinition; $diCompiler->addDirectory(’/path/to/classes/’ . str_replace(’_’, ’/’, $component)); 10 $diCompiler->compile(); file_put_contents( __DIR__ . ’/../data/di/’ . $component . ’-definition.php’, ’<?php return ’ . var_export($diCompiler->toArrayDefinition()->toArray(), true) . ’;’ ); 11 12 13 14 15 16 } This will create a couple of files that will return an array of the definition for that class. To utilize this in an application, the following code will suffice: 1 2 3 protected function setupDi(Application $app) { $definitionList = new DefinitionList(array( 352 Chapter 80. ZendDi Definition
  • 391. Zend Framework 2 Documentation, Release 2.2.5 new DefinitionArrayDefinition(include __DIR__ . ’/path/to/data/di/My_MovieApp-definition.php new DefinitionArrayDefinition(include __DIR__ . ’/path/to/data/di/My_OtherClasses-definition $runtime = new DefinitionRuntimeDefinition(), 4 5 6 )); $di = new Di($definitionList, null, new Configuration($this->config->di)); $di->instanceManager()->addTypePreference(’ZendDiLocatorInterface’, $di); $app->setLocator($di); 7 8 9 10 11 } The above code would more than likely go inside your application’s or module’s bootstrap file. This represents the simplest and most performant way of configuring your DiC for usage. 80.4 ClassDefinition The idea behind using a ClassDefinition is two-fold. First, you may want to override some information inside of a RuntimeDefinition. Secondly, you might want to simply define your complete class’s definition with an xml, ini, or php file describing the structure. This class definition can be fed in via Configuration or by directly instantiating and registering the Definition with the DefinitionList. Todo - example 80.4. ClassDefinition 353
  • 392. Zend Framework 2 Documentation, Release 2.2.5 354 Chapter 80. ZendDi Definition
  • 393. CHAPTER 81 ZendDi InstanceManager The InstanceManager is responsible for any runtime information associated with the ZendDiDi DiC. This means that the information that goes into the instance manager is specific to both how the particular consuming Application’s needs and even more specifically to the environment in which the application is running. 81.1 Parameters Parameters are simply entry points for either dependencies or instance configuration values. A class consists of a set of parameters, each uniquely named. When writing your classes, you should attempt to not use the same parameter name twice in the same class when you expect that that parameters is used for either instance configuration or an object dependency. This leads to an ambiguous parameter, and is a situation best avoided. Our movie finder example can be further used to explain these concepts: 1 2 3 4 5 6 7 8 9 10 11 12 13 namespace MyLibrary { class DbAdapter { protected $username = null; protected $password = null; public function __construct($username, $password) { $this->username = $username; $this->password = $password; } } } 14 15 16 17 18 19 20 21 22 23 24 namespace MyMovieApp { class MovieFinder { protected $dbAdapter = null; public function __construct(MyLibraryDbAdapter $dbAdapter) { $this->dbAdapter = $dbAdapter; } } 355
  • 394. Zend Framework 2 Documentation, Release 2.2.5 25 class MovieLister { protected $movieFinder = null; public function __construct(MovieFinder $movieFinder) { $this->movieFinder = $movieFinder; } } 26 27 28 29 30 31 32 33 34 } In the above example, the class DbAdapter has 2 parameters: username and password; MovieFinder has one parameter: dbAdapter, and MovieLister has one parameter: movieFinder. Any of these can be utilized for injection of either dependencies or scalar values during instance configuration or during call time. When looking at the above code, since the dbAdapter parameter and the movieFinder parameter are both type-hinted with concrete types, the DiC can assume that it can fulfill these object tendencies by itself. On the other hand, username and password do not have type-hints and are, more than likely, scalar in nature. Since the DiC cannot reasonably know this information, it must be provided to the instance manager in the form of parameters. Not doing so will force $di->get(‘MyMovieAppMovieLister’) to throw an exception. The following ways of using parameters are available: 1 2 3 4 5 // setting instance configuration into the instance manager $di->instanceManager()->setParameters(’MyLibraryDbAdapter’, array( ’username’ => ’myusername’, ’password’ => ’mypassword’ )); 6 7 8 9 10 // forcing a particular dependency to be used by the instance manager $di->instanceManager()->setParameters(’MyMovieAppMovieFinder’, array( ’dbAdapter’ => new MyLibraryDbAdapter(’myusername’, ’mypassword’) )); 11 12 13 14 15 16 // passing instance parameters at call time $movieLister = $di->get(’MyMovieAppMovieLister’, array( ’username’ => $config->username, ’password’ => $config->password )); 17 18 19 20 21 // passing a specific instance at call time $movieLister = $di->get(’MyMovieAppMovieLister’, array( ’dbAdapter’ => new MyLibraryDbAdapter(’myusername’, ’mypassword’) )); 81.2 Preferences In some cases, you might be using interfaces as type hints as opposed to concrete types. Lets assume the movie example was modified in the following way: 1 2 3 4 5 6 namespace MyMovieApp { interface MovieFinderInterface { // methods required for this type } 7 356 Chapter 81. ZendDi InstanceManager
  • 395. Zend Framework 2 Documentation, Release 2.2.5 class GenericMovieFinder implements MovieFinderInterface { protected $dbAdapter = null; public function __construct(MyLibraryDbAdapter $dbAdapter) { $this->dbAdapter = $dbAdapter; } } 8 9 10 11 12 13 14 15 16 class MovieLister { protected $movieFinder = null; public function __construct(MovieFinderInterface $movieFinder) { $this->movieFinder = $movieFinder; } } 17 18 19 20 21 22 23 24 25 } What you’ll notice above is that now the MovieLister type minimally expects that the dependency injected implements the MovieFinderInterface. This allows multiple implementations of this base interface to be used as a dependency, if that is what the consumer decides they want to do. As you can imagine, ZendDi, by itself would not be able to determine what kind of concrete object to use fulfill this dependency, so this type of ‘preference’ needs to be made known to the instance manager. To give this information to the instance manager, see the following code example: 1 2 3 $di->instanceManager()->addTypePreference(’MyMovieAppMovieFinderInterface’, ’MyMovieAppGenericMovie // assuming all instance config for username, password is setup $di->get(’MyMovieAppMovieLister’); 81.3 Aliases In some situations, you’ll find that you need to alias an instance. There are two main reasons to do this. First, it creates a simpler, alternative name to use when using the DiC, as opposed to using the full class name. Second, you might find that you need to have the same object type in two separate contexts. This means that when you alias a particular class, you can then attach a specific instance configuration to that alias; as opposed to attaching that configuration to the class name. To demonstrate both of these points, we’ll look at a use case where we’ll have two separate DbAdapters, one will be for read-only operations, the other will be for read-write operations: Note: Aliases can also have parameters registered at alias time 1 // assume the MovieLister example of code from the QuickStart 2 3 $im = $di->instanceManager(); 4 5 6 // add alias for short naming $im->addAlias(’movielister’, ’MyMovieAppMovieLister’); 7 8 9 10 11 // add aliases for specific instances $im->addAlias(’dbadapter-readonly’, ’MyLibraryDbAdapter’, array( ’username’ => $config->db->readAdapter->username, ’password’ => $config->db->readAdapter->password, 81.3. Aliases 357
  • 396. Zend Framework 2 Documentation, Release 2.2.5 12 13 14 15 16 )); $im->addAlias(’dbadapter-readwrite’, ’MyLibraryDbAdapter’, array( ’username’ => $config->db->readWriteAdapter->username, ’password’ => $config->db->readWriteAdapter->password, )); 17 18 19 // set a default type to use, pointing to an alias $im->addTypePreference(’MyLibraryDbAdapter’, ’dbadapter-readonly’); 20 21 22 $movieListerRead = $di->get(’MyMovieAppMovieLister’); $movieListerReadWrite = $di->get(’MyMovieAppMovieLister’, array(’dbAdapter’ => ’dbadapter-readwrite’ 358 Chapter 81. ZendDi InstanceManager
  • 397. CHAPTER 82 ZendDi Configuration Most of the configuration for both the setup of Definitions as well as the setup of the Instance Manager can be attained by a configuration file. This file will produce an array (typically) and have a particular iterable structure. The top two keys are ‘definition’ and ‘instance’, each specifying values for respectively, definition setup and instance manager setup. The definition section expects the following information expressed as a PHP array: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $config = array( ’definition’ => array( ’compiler’ => array(/* @todo compiler information */), ’runtime’ => array(/* @todo runtime information */), ’class’ => array( ’instantiator’ => ’’, // the name of the instantiator, by default this is __construct ’supertypes’ => array(), // an array of supertypes the class implements ’methods’ => array( ’setSomeParameter’ => array( // a method name ’parameterName’ => array( ’name’, // string parameter name ’type’, // type or null ’is-required’ // bool ) ) 16 ) 17 ) 18 ) 19 20 ); 359
  • 398. Zend Framework 2 Documentation, Release 2.2.5 360 Chapter 82. ZendDi Configuration
  • 399. CHAPTER 83 ZendDi Debugging & Complex Use Cases 83.1 Debugging a DiC It is possible to dump the information contained within both the Definition and InstanceManager for a Di instance. The easiest way is to do the following: 1 ZendDiDisplayConsole::export($di); If you are using a RuntimeDefinition where upon you expect a particular definition to be resolve at the first-call, you can see that information to the console display to force it to read that class: 1 ZendDiDisplayConsole::export($di, array(’AClassIWantToGetTheDefinitionFor’)); 83.2 Complex Use Cases 83.2.1 Interface Injection 1 2 3 4 namespace FooBar { class Baz implements BamAwareInterface { public $bam; 5 6 7 8 9 10 11 12 13 14 15 16 public function setBam(Bam $bam) { $this->bam = $bam; } } class Bam { } interface BamAwareInterface { public function setBam(Bam $bam); 361
  • 400. Zend Framework 2 Documentation, Release 2.2.5 } 17 18 } 19 20 21 22 23 24 namespace { include ’zf2bootstrap.php’; $di = new ZendDiDi; $baz = $di->get(’FooBarBaz’); } 83.2.2 Setter Injection with Class Definition 1 2 3 4 namespace FooBar { class Baz { public $bam; 5 public function setBam(Bam $bam) { $this->bam = $bam; } 6 7 8 9 } class Bam { } 10 11 12 13 } 14 15 16 17 18 19 20 21 22 23 24 25 26 27 namespace { $di = new ZendDiDi; $di->configure(new ZendDiConfig(array( ’definition’ => array( ’class’ => array( ’FooBarBaz’ => array( ’setBam’ => array(’required’ => true) ) ) ) ))); $baz = $di->get(’FooBarBaz’); } 83.2.3 Multiple Injections To A Single Injection Point 1 2 3 4 namespace Application { class Page { public $blocks; 5 public function addBlock(Block $block) { $this->blocks[] = $block; } 6 7 8 9 } interface Block { } 10 11 12 13 14 } 362 Chapter 83. ZendDi Debugging & Complex Use Cases
  • 401. Zend Framework 2 Documentation, Release 2.2.5 15 16 17 18 19 namespace MyModule { class BlockOne implements ApplicationBlock {} class BlockTwo implements ApplicationBlock {} } 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 namespace { include ’zf2bootstrap.php’; $di = new ZendDiDi; $di->configure(new ZendDiConfig(array( ’definition’ => array( ’class’ => array( ’ApplicationPage’ => array( ’addBlock’ => array( ’block’ => array(’type’ => ’ApplicationBlock’, ’required’ => true) ) ) ) ), ’instance’ => array( ’ApplicationPage’ => array( ’injections’ => array( ’MyModuleBlockOne’, ’MyModuleBlockTwo’ ) ) ) ))); $page = $di->get(’ApplicationPage’); } 83.2. Complex Use Cases 363
  • 402. Zend Framework 2 Documentation, Release 2.2.5 364 Chapter 83. ZendDi Debugging & Complex Use Cases
  • 403. CHAPTER 84 Introduction to ZendDom The ZendDom component provides tools for working with DOM documents and structures. Currently, we offer ZendDomQuery, which provides a unified interface for querying DOM documents utilizing both XPath and CSS selectors. 365
  • 404. Zend Framework 2 Documentation, Release 2.2.5 366 Chapter 84. Introduction to ZendDom
  • 405. CHAPTER 85 ZendDomQuery ZendDomQuery provides mechanisms for querying XML and (X) HTML documents utilizing either XPath or CSS selectors. It was developed to aid with functional testing of MVC applications, but could also be used for rapid development of screen scrapers. CSS selector notation is provided as a simpler and more familiar notation for web developers to utilize when querying documents with XML structures. The notation should be familiar to anybody who has developed Cascading Style Sheets or who utilizes Javascript toolkits that provide functionality for selecting nodes utilizing CSS selectors (Prototype’s $$() and Dojo’s dojo.query were both inspirations for the component). 85.1 Theory of Operation To use ZendDomQuery, you instantiate a ZendDomQuery object, optionally passing a document to query (a string). Once you have a document, you can use either the query() or queryXpath() methods; each method will return a ZendDomNodeList object with any matching nodes. The primary difference between ZendDomQuery and using DOMDocument + DOMXPath is the ability to select against CSS selectors. You can utilize any of the following, in any combination: • element types: provide an element type to match: ‘div’, ‘a’, ‘span’, ‘h2’, etc. • style attributes: CSS style attributes to match: ‘.error‘, ‘div.error‘, ‘label.required‘, etc. If an element defines more than one style, this will match as long as the named style is present anywhere in the style declaration. • id attributes: element ID attributes to match: ‘#content’, ‘div#nav’, etc. • arbitrary attributes: arbitrary element attributes to match. Three different types of matching are provided: – exact match: the attribute exactly matches the string: ‘div[bar=”baz”]’ would match a div element with a “bar” attribute that exactly matches the value “baz”. – word match: the attribute contains a word matching the string: ‘div[bar~=”baz”]’ would match a div element with a “bar” attribute that contains the word “baz”. ‘<div bar=”foo baz”>’ would match, but ‘<div bar=”foo bazbat”>’ would not. – substring match: the attribute contains the string: ‘div[bar*=”baz”]’ would match a div element with a “bar” attribute that contains the string “baz” anywhere within it. • direct descendents: utilize ‘>’ between selectors to denote direct descendents. ‘div > span’ would select only ‘span’ elements that are direct descendents of a ‘div’. Can also be used with any of the selectors above. 367
  • 406. Zend Framework 2 Documentation, Release 2.2.5 • descendents: string together multiple selectors to indicate a hierarchy along which to search. ‘div .foo span #one‘ would select an element of id ‘one’ that is a descendent of arbitrary depth beneath a ‘span’ element, which is in turn a descendent of arbitrary depth beneath an element with a class of ‘foo’, that is an descendent of arbitrary depth beneath a ‘div’ element. For example, it would match the link to the word ‘One’ in the listing below: <div> <table> <tr> <td class="foo"> <div> Lorem ipsum <span class="bar"> <a href="/foo/bar" id="one">One</a> <a href="/foo/baz" id="two">Two</a> <a href="/foo/bat" id="three">Three</a> <a href="/foo/bla" id="four">Four</a> </span> </div> </td> </tr> </table> </div> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Once you’ve performed your query, you can then work with the result object to determine information about the nodes, as well as to pull them and/or their content directly for examination and manipulation. ZendDomNodeList implements Countable and Iterator, and stores the results internally as a DOMDocument and DOMNodeList. As an example, consider the following call, that selects against the HTML above: 1 use ZendDomQuery; 2 3 4 $dom = new Query($html); $results = $dom->execute(’.foo .bar a’); 5 6 7 8 9 $count = count($results); // get number of matches: 4 foreach ($results as $result) { // $result is a DOMElement } ZendDomQuery also allows straight XPath queries utilizing the queryXpath() method; you can pass any valid XPath query to this method, and it will return a ZendDomNodeList object. 85.2 Methods Available The ZendDomQuery family of classes have the following methods available. 85.2.1 ZendDomQuery The following methods are available to ZendDomQuery: • setDocumentXml($document, $encoding = null): specify an XML string to query against. • setDocumentXhtml($document, $encoding = null): specify an XHTML string to query against. • setDocumentHtml($document, $encoding = null): specify an HTML string to query against. • setDocument($document, $encoding = null): specify ZendDomQuery will then attempt to autodetect the document type. 368 a string to query against; Chapter 85. ZendDomQuery
  • 407. Zend Framework 2 Documentation, Release 2.2.5 • setEncoding($encoding): specify an encoding string to use. This encoding will be passed to DOMDocument’s constructor if specified. • getDocument(): retrieve the original document string provided to the object. • getDocumentType(): retrieve the document type of the document provided to the object; will be one of the DOC_XML, DOC_XHTML, or DOC_HTML class constants. • getEncoding(): retrieves the specified encoding. • execute($query): query the document using CSS selector notation. • queryXpath($xPathQuery): query the document using XPath notation. 85.2.2 ZendDomNodeList As mentioned previously, ZendDomNodeList implements both Iterator and Countable, and as such can be used in a foreach() loop as well as with the count() function. Additionally, it exposes the following methods: • getCssQuery(): return the CSS selector query used to produce the result (if any). • getXpathQuery(): return the XPath query used to produce the result. Internally, ZendDomQuery converts CSS selector queries to XPath, so this value will always be populated. • getDocument(): retrieve the DOMDocument the selection was made against. 85.2. Methods Available 369
  • 408. Zend Framework 2 Documentation, Release 2.2.5 370 Chapter 85. ZendDomQuery
  • 409. CHAPTER 86 Introduction to ZendEscaper The OWASP Top 10 web security risks study lists Cross-Site Scripting (XSS) in second place. PHP’s sole functionality against XSS is limited to two functions of which one is commonly misapplied. Thus, the ZendEscaper component was written. It offers developers a way to escape output and defend from XSS and related vulnerabilities by introducing contextual escaping based on peer-reviewed rules. ZendEscaper was written with ease of use in mind, so it can be used completely stand-alone from the rest of the framework, and as such can be installed with Composer. For easier use of the Escaper component within the framework itself, especially with the ZendView component, a set of view helpers is provided. Warning: The ZendEscaper is a security related component. As such, if you believe you found an issue with this component, we ask that you follow our Security Policy and report security issues accordingly. The Zend Framework team and the contributors thanks you in advance. 86.1 Overview The ZendEscaper component provides one class, ZendEscaperEscaper which in turn, provides five methods for escaping output. Which method to use when, depends on the context in which the outputted data is used. It is up to the developer to use the right methods in the right context. ZendEscaperEscaper has the following escaping methods available for each context: • escapeHtml: escape a string for the HTML Body context. • escapeHtmlAttr: escape a string for the HTML Attribute context. • escapeJs: escape a string for the Javascript context. • escapeCss: escape a string for the CSS context. • escapeUrl: escape a string for the URI or Parameter contexts. Usage of each method will be discussed in detail in later chapters. 371
  • 410. Zend Framework 2 Documentation, Release 2.2.5 86.2 What ZendEscaper is not ZendEscaper is meant to be used only for escaping data that is to be output, and as such should not be misused for filtering input data. For such tasks, the ZendFilter component, HTMLPurifier or PHP’s Filter component should be used. 372 Chapter 86. Introduction to ZendEscaper
  • 411. CHAPTER 87 Theory of Operation ZendEscaper provides methods for escaping output data, dependent on the context in which the data will be used. Each method is based on peer-reviewed rules and is in compliance with the current OWASP recommendations. The escaping follows a well known and fixed set of encoding rules for each key HTML context, which are defined by OWASP. These rules cannot be impacted or negated by browser quirks or edge-case HTML parsing unless the browser suffers a catastrophic bug in it’s HTML parser or Javascript interpreter - both of these are unlikely. The contexts in which ZendEscaper should be used are HTML Body, HTML Attribute, Javascript, CSS and URL/URI contexts. Every escaper method will take the data to be escaped, make sure it is utf-8 encoded data, or try to convert it to utf-8, do the context-based escaping, encode the escaped data back to it’s original encoding and return the data to the caller. The actual escaping of the data differs between each method, they all have their own set of rules according to which the escaping is done. An example will allow us to clearly demonstrate the difference, and how the same characters are being escaped differently between contexts: 1 $escaper = new ZendEscaperEscaper(’utf-8’); 2 3 4 5 6 7 8 9 10 11 12 // &lt;script&gt;alert(&quot;zf2&quot;)&lt;/script&gt; echo $escaper->escapeHtml(’<script>alert("zf2")</script>’); // &lt;script&gt;alert&#x28;&quot;zf2&quot;&#x29;&lt;&#x2F;script&gt; echo $escaper->escapeHtmlAttr(’<script>alert("zf2")</script>’); // x3Cscriptx3Ealertx28x22zf2x22x29x3Cx2Fscriptx3E echo $escaper->escapeJs(’<script>alert("zf2")</script>’); // 3C script3E alert28 22 zf222 29 3C 2F script3E echo $escaper->escapeCss(’<script>alert("zf2")</script>’); // %3Cscript%3Ealert%28%22zf2%22%29%3C%2Fscript%3E echo $escaper->escapeUrl(’<script>alert("zf2")</script>’); More detailed examples will be given in later chapters. 87.1 The Problem with Inconsistent Functionality At present, programmers orient towards the following PHP functions for each common HTML context: • HTML Body: htmlspecialchars() or htmlentities() • HTML Attribute: htmlspecialchars() or htmlentities() 373
  • 412. Zend Framework 2 Documentation, Release 2.2.5 • Javascript: addslashes() or json_encode() • CSS: n/a • URL/URI: rawurlencode() or urlencode() In practice, these decisions appear to depend more on what PHP offers, and if it can be interpreted as offering sufficient escaping safety, than it does on what is recommended in reality to defend against XSS. While these functions can prevent some forms of XSS, they do not cover all use cases or risks and are therefore insufficient defenses. Using htmlspecialchars() in a perfectly valid HTML5 unquoted attribute value, for example, is completely useless since the value can be terminated by a space (among other things) which is never escaped. Thus, in this instance, we have a conflict between a widely used HTML escaper and a modern HTML specification, with no specific function available to cover this use case. While it’s tempting to blame users, or the HTML specification authors, escaping just needs to deal with whatever HTML and browsers allow. Using addslashes(), custom backslash escaping or json_encode() will typically ignore HTML special characters such as ampersands which may be used to inject entities into Javascript. Under the right circumstances, browser will convert these entities into their literal equivalents before interpreting Javascript thus allowing attackers to inject arbitrary code. Inconsistencies with valid HTML, insecure default parameters, lack of character encoding awareness, and misrepresentations of what functions are capable of by some programmers - these all make escaping in PHP an unnecessarily convoluted quest. To circumvent the lack of escaping methods in PHP, ZendEscaper addresses the need to apply context-specific escaping in web applications. It implements methods that specifically target XSS and offers programmers a tool to secure their applications without misusing other inadequate methods, or using, most likely incomplete, home-grown solutions. 87.2 Why Contextual Escaping? To understand why multiple standardised escaping methods are needed, here’s a couple of quick points (by no means a complete set!): 87.2.1 HTML escaping of unquoted HTML attribute values still allows XSS This is probably the best known way to defeat htmlspecialchars() when used on attribute values since any space (or character interpreted as a space - there are a lot) lets you inject new attributes whose content can’t be neutralised by HTML escaping. The solution (where this is possible) is additional escaping as defined by the OWASP ESAPI codecs. The point here can be extended further - escaping only works if a programmer or designer know what they’re doing. In many contexts, there are additional practices and gotchas that need to be carefully monitored since escaping sometimes needs a little extra help to protect against XSS - even if that means ensuring all attribute values are properly double quoted despite this not being required for valid HTML. 87.2.2 HTML escaping of CSS, Javascript or URIs is often reversed when passed to non-HTML interpreters by the browser HTML escaping is just that - it’s designed to escape a string for HTML (i.e. prevent tag or attribute insertion) but not alter the underlying meaning of the content whether it be Text, Javascript, CSS or URIs. For that purpose a fully HTML escaped version of any other context may still have its unescaped form extracted before it’s interpreted or executed. For this reason we need separate escapers for Javascript, CSS and URIs and those writing templates must know which escaper to apply to which context. Of course this means you need to be able to identify the correct context before selecting the right escaper! 374 Chapter 87. Theory of Operation
  • 413. Zend Framework 2 Documentation, Release 2.2.5 87.2.3 DOM based XSS requires a defence using at least two levels of different escaping in many cases DOM based XSS has become increasingly common as Javascript has taken off in popularity for large scale client side coding. A simple example is Javascript defined in a template which inserts a new piece of HTML text into the DOM. If the string is only HTML escaped, it may still contain Javascript that will execute in that context. If the string is only Javascript escaped, it may contain HTML markup (new tags and attributes) which will be injected into the DOM and parsed once the inserting Javascript executes. Damned either way? The solution is to escape twice - first escape the string for HTML (make it safe for DOM insertion), and then for Javascript (make it safe for the current Javascript context). Nested contexts are a common means of bypassing naive escaping habits (e.g. you can inject Javascript into a CSS expression within a HTML Attribute). 87.2.4 PHP has no known anti-XSS escape functions (only those kidnapped from their original purposes) A simple example, widely used, is when you see json_encode() used to escape Javascript, or worse, some kind of mutant addslashes() implementation. These were never designed to eliminate XSS yet PHP programmers use them as such. For example, json_encode() does not escape the ampersand or semi-colon characters by default. That means you can easily inject HTML entities which could then be decoded before the Javascript is evaluated in a HTML document. This lets you break out of strings, add new JS statements, close tags, etc. In other words, using json_encode() is insufficient and naive. The same, arguably, could be said for htmlspecialchars() which has its own well known limitations that make a singular reliance on it a questionable practice. 87.2. Why Contextual Escaping? 375
  • 414. Zend Framework 2 Documentation, Release 2.2.5 376 Chapter 87. Theory of Operation
  • 415. CHAPTER 88 Configuring ZendEscaper ZendEscaperEscaper has only one configuration option available, and that is the encoding to be used by the Escaper object. The default encoding is utf-8. Other supported encodings are: • iso-8859-1 • iso-8859-5 • iso-8859-15 • cp866, ibm866, 866 • cp1251, windows-1251 • cp1252, windows-1252 • koi8-r, koi8-ru • big5, big5-hkscs, 950, gb2312, 936 • shift_jis, sjis, sjis-win, cp932 • eucjp, eucjp-win • macroman If an unsupported encoding is passed to ZendEscaperEscaper, ZendEscaperExceptionInvalidArgumentException will be thrown. a 377
  • 416. Zend Framework 2 Documentation, Release 2.2.5 378 Chapter 88. Configuring ZendEscaper
  • 417. CHAPTER 89 Escaping HTML Probably the most common escaping happens in the HTML Body context. There are very few characters with special meaning in this context, yet it is quite common to escape data incorrectly, namely by setting the wrong flags and character encoding. For escaping data in the HTML Body context, use ZendEscaperEscaper‘s escapeHtml method. Internally it uses PHP’s htmlspecialchars, and additionally correctly sets the flags and encoding. 1 2 // outputting this without escaping would be a bad idea! $input = ’<script>alert("zf2")</script>’; 3 4 $escaper = new ZendEscaperEscaper(’utf-8’); 5 6 7 8 9 10 11 // somewhere in an HTML template <div class="user-provided-input"> <?php echo $escaper->escapeHtml($input); // all safe! ?> </div> One thing a developer needs to pay special attention too, is that the encoding in which the document is served to the client, as it must be the same as the encoding used for escaping! 89.1 Examples of Bad HTML Escaping An example of incorrect usage: 1 2 3 4 5 6 7 8 9 10 11 12 <?php $input = ’<script>alert("zf2")</script>’; $escaper = new ZendEscaperEscaper(’utf-8’); ?> <?php header(’Content-Type: text/html; charset=ISO-8859-1’); ?> <!DOCTYPE html> <html> <head> <title>Encodings set incorrectly!</title> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> </head> <body> 379
  • 418. Zend Framework 2 Documentation, Release 2.2.5 13 14 15 16 17 <?php // Bad! The escaper’s and the document’s encodings are different! echo $escaper->escapeHtml($input); ?> </body> 89.2 Examples of Good HTML Escaping An example of correct usage: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php $input = ’<script>alert("zf2")</script>’; $escaper = new ZendEscaperEscaper(’utf-8’); ?> <?php header(’Content-Type: text/html; charset=UTF-8’); ?> <!DOCTYPE html> <html> <head> <title>Encodings set correctly!</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body> <?php // Good! The escaper’s and the document’s encodings are same! echo $escaper->escapeHtml($input); ?> </body> 380 Chapter 89. Escaping HTML
  • 419. CHAPTER 90 Escaping HTML Attributes Escaping data in the HTML Attribute context is most often done incorrectly, if not overlooked completely by developers. Regular HTML escaping can be used for escaping HTML attributes, but only if the attribute value can be guaranteed as being properly quoted! To avoid confusion, we recommend always using the HTML Attribute escaper method in the HTML Attribute context. To escape data in the HTML Attribute, use ZendEscaperEscaper‘s escapeHtmlAttr method. Internally it will convert the data to UTF-8, check for it’s validity, and use an extended set of characters to escape that are not covered by htmlspecialchars to cover the cases where an attribute might be unquoted or quoted illegally. 90.1 Examples of Bad HTML Attribute Escaping An example of incorrect HTML attribute escaping: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?php header(’Content-Type: text/html; charset=UTF-8’); ?> <!DOCTYPE html> <?php $input = <<<INPUT ’ onmouseover=’alert(/ZF2!/); INPUT; /** * NOTE: This is equivalent to using htmlspecialchars($input, ENT_COMPAT) */ $output = htmlspecialchars($input); ?> <html> <head> <title>Single Quoted Attribute</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body> <div> <?php // the span tag will look like: // <span title=’’ onmouseover=’alert(/ZF2!/);’> ?> <span title=’<?php echo $output ?>’> What framework are you using? 381
  • 420. Zend Framework 2 Documentation, Release 2.2.5 25 26 27 28 </span> </div> </body> </html> In the above example, the default ENT_COMPAT flag is being used, which does not escape single quotes, thus resulting in an alert box popping up when the onmouseover event happens on the span element. Another example of incorrect HTML attribute escaping can happen when unquoted attributes are used, which is, by the way, perfectly valid HTML5: 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 <?php header(’Content-Type: text/html; charset=UTF-8’); ?> <!DOCTYPE html> <?php $input = <<<INPUT faketitle onmouseover=alert(/ZF2!/); INPUT; // Tough luck using proper flags when the title attribute is unquoted! $output = htmlspecialchars($input,ENT_QUOTES); ?> <html> <head> <title>Quoteless Attribute</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body> <div> <?php // the span tag will look like: // <span title=faketitle onmouseover=alert(/ZF2!/);> ?> <span title=<?php echo $output ?>> What framework are you using? </span> </div> </body> </html> The above example shows how it is easy to break out from unquoted attributes in HTML5. 90.2 Examples of Good HTML Attribute Escaping Both of the previous examples can be avoided by simply using the escapeHtmlAttr method: 1 2 3 4 5 6 7 8 9 10 11 12 13 <?php header(’Content-Type: text/html; charset=UTF-8’); ?> <!DOCTYPE html> <?php $input = <<<INPUT faketitle onmouseover=alert(/ZF2!/); INPUT; $escaper = new ZendEscaperEscaper(’utf-8’); $output = $escaper->escapeHtmlAttr($input); ?> <html> <head> <title>Quoteless Attribute</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 382 Chapter 90. Escaping HTML Attributes
  • 421. Zend Framework 2 Documentation, Release 2.2.5 14 15 16 17 18 19 20 21 22 23 24 25 26 </head> <body> <div> <?php // the span tag will look like: // <span title=faketitle&#x20;onmouseover&#x3D;alert&#x28;&#x2F;ZF2&#x21;&#x2F;&#x29;&#x3B;> ?> <span title=<?php echo $output ?>> What framework are you using? </span> </div> </body> </html> In the above example, the malicious input from the attacker becomes completely harmless as we used proper HTML attribute escaping! 90.2. Examples of Good HTML Attribute Escaping 383
  • 422. Zend Framework 2 Documentation, Release 2.2.5 384 Chapter 90. Escaping HTML Attributes
  • 423. CHAPTER 91 Escaping Javascript Javascript string literals in HTML are subject to significant restrictions particularly due to the potential for unquoted attributes and any uncertainty as to whether Javascript will be viewed as being CDATA or PCDATA by the browser. To eliminate any possible XSS vulnerabilities, Javascript escaping for HTML extends the escaping rules of both ECMAScript and JSON to include any potentially dangerous character. Very similar to HTML attribute value escaping, this means escaping everything except basic alphanumeric characters and the comma, period and underscore characters as hexadecimal or unicode escapes. Javascript escaping applies to all literal strings and digits. It is not possible to safely escape other Javascript markup. To escape data in the Javascript context, use ZendEscaperEscaper‘s escapeJs method. An extended set of characters are escaped beyond ECMAScript’s rules for Javascript literal string escaping in order to prevent misinterpretation of Javascript as HTML leading to the injection of special characters and entities. 91.1 Examples of Bad Javascript Escaping An example of incorrect Javascript escaping: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php header(’Content-Type: application/xhtml+xml; charset=UTF-8’); ?> <!DOCTYPE html> <?php $input = <<<INPUT bar&quot;; alert(&quot;Meow!&quot;); var xss=&quot;true INPUT; $output = json_encode($input); ?> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Unescaped Entities</title> <meta charset="UTF-8"/> <script type="text/javascript"> <?php // this will result in // var foo = "bar&quot;; alert(&quot;Meow!&quot;); var xss=&quot;true"; ?> var foo = <?php echo $output ?>; </script> </head> <body> 385
  • 424. Zend Framework 2 Documentation, Release 2.2.5 22 23 24 <p>json_encode() is not good for escaping javascript!</p> </body> </html> The above example will show an alert popup box as soon as the page is loaded, because the data is not properly escaped for the Javascript context. 91.2 Examples of Good Javascript Escaping By using the escapeJs method in the Javascript context, such attacks can be prevented: 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 <?php header(’Content-Type: text/html; charset=UTF-8’); ?> <!DOCTYPE html> <?php $input = <<<INPUT bar&quot;; alert(&quot;Meow!&quot;); var xss=&quot;true INPUT; $escaper = new ZendEscaperEscaper(’utf-8’); $output = $escaper->escapeJs($input); ?> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Escaped Entities</title> <meta charset="UTF-8"/> <script type="text/javascript"> <?php // this will look like // var foo = barx26quotx3Bx3Bx20alertx28x26quotx3BMeowx21x26quotx3Bx29x3Bx20var ?> var foo = <?php echo $output ?>; </script> </head> <body> <p>ZendEscaperEscaper::escapeJs() is good for escaping javascript!</p> </body> </html> In the above example, the Javascript parser will most likely report a SyntaxError, but at least the targeted application remains safe from such attacks. 386 Chapter 91. Escaping Javascript
  • 425. CHAPTER 92 Escaping Cascading Style Sheets CSS is similar to Javascript for the same reasons. CSS escaping excludes only basic alphanumeric characters and escapes all other characters into valid CSS hexadecimal escapes. 92.1 Examples of Bad CSS Escaping In most cases developers forget to escape CSS completely: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php header(’Content-Type: application/xhtml+xml; charset=UTF-8’); ?> <!DOCTYPE html> <?php $input = <<<INPUT body { background-image: url(’http://example.com/foo.jpg?</style><script>alert(1)</script>’); } INPUT; ?> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Unescaped CSS</title> <meta charset="UTF-8"/> <style> <?php echo $input; ?> </style> </head> <body> <p>User controlled CSS needs to be properly escaped!</p> </body> </html> In the above example, by failing to escape the user provided CSS, an attacker can execute an XSS attack fairly easily. 92.2 Examples of Good CSS Escaping By using escapeCss method in the CSS context, such attacks can be prevented: 387
  • 426. Zend Framework 2 Documentation, Release 2.2.5 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 <?php header(’Content-Type: application/xhtml+xml; charset=UTF-8’); ?> <!DOCTYPE html> <?php $input = <<<INPUT body { background-image: url(’http://example.com/foo.jpg?</style><script>alert(1)</script>’); } INPUT; $escaper = new ZendEscaperEscaper(’utf-8’); $output = $escaper->escapeCss($input); ?> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Escaped CSS</title> <meta charset="UTF-8"/> <style> <?php // output will look something like // body20 7B A 20 20 20 20 background2D image3A 20 url28 ... echo $output; ?> </style> </head> <body> <p>User controlled CSS needs to be properly escaped!</p> </body> </html> By properly escaping user controlled CSS, we can prevent XSS attacks in our web applications. 388 Chapter 92. Escaping Cascading Style Sheets
  • 427. CHAPTER 93 Escaping URLs This method is basically an alias for PHP’s rawurlencode() which has applied RFC 3986 since PHP 5.3. It is included primarily for consistency. URL escaping applies to data being inserted into a URL and not to the whole URL itself. 93.1 Examples of Bad URL Escaping XSS attacks are easy if data inserted into URLs is not escaped properly: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php header(’Content-Type: application/xhtml+xml; charset=UTF-8’); ?> <!DOCTYPE html> <?php $input = <<<INPUT " onmouseover="alert(’zf2’) INPUT; ?> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Unescaped URL data</title> <meta charset="UTF-8"/> </head> <body> <a href="http://example.com/?name=<?php echo $input; ?>">Click here!</a> </body> </html> 93.2 Examples of Good URL Escaping By properly escaping data in URLs by using escapeUrl, we can prevent XSS attacks: 1 2 3 4 5 6 <?php header(’Content-Type: application/xhtml+xml; charset=UTF-8’); ?> <!DOCTYPE html> <?php $input = <<<INPUT " onmouseover="alert(’zf2’) INPUT; 389
  • 428. Zend Framework 2 Documentation, Release 2.2.5 7 8 9 10 11 12 13 14 15 16 17 18 $escaper = new ZendEscaperEscaper(’utf-8’); $output = $escaper->escapeUrl($input); ?> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Unescaped URL data</title> <meta charset="UTF-8"/> </head> <body> <a href="http://example.com/?name=<?php echo $output; ?>">Click here!</a> </body> </html> 390 Chapter 93. Escaping URLs
  • 429. CHAPTER 94 The EventManager 94.1 Overview The EventManager is a component designed for the following use cases: • Implementing simple subject/observer patterns. • Implementing Aspect-Oriented designs. • Implementing event-driven architectures. The basic architecture allows you to attach and detach listeners to named events, both on a per-instance basis as well as via shared collections; trigger events; and interrupt execution of listeners. 94.2 Quick Start Typically, you will compose an EventManager instance in a class. 1 2 3 use ZendEventManagerEventManagerInterface; use ZendEventManagerEventManager; use ZendEventManagerEventManagerAwareInterface; 4 5 6 7 class Foo implements EventManagerAwareInterface { protected $events; 8 9 10 11 12 13 14 15 16 17 public function setEventManager(EventManagerInterface $events) { $events->setIdentifiers(array( __CLASS__, get_called_class(), )); $this->events = $events; return $this; } 18 19 20 21 public function getEventManager() { if (null === $this->events) { 391
  • 430. Zend Framework 2 Documentation, Release 2.2.5 $this->setEventManager(new EventManager()); } return $this->events; 22 23 24 } 25 26 } The above allows users to access the EventManager instance, or reset it with a new instance; if one does not exist, it will be lazily instantiated on-demand. An EventManager is really only interesting if it triggers some events. Basic triggering takes three arguments: the event name, which is usually the current function/method name; the “context”, which is usually the current object instance; and the arguments, which are usually the arguments provided to the current function/method. 1 2 3 class Foo { // ... assume events definition from above 4 public function bar($baz, $bat = null) { $params = compact(’baz’, ’bat’); $this->getEventManager()->trigger(__FUNCTION__, $this, $params); } 5 6 7 8 9 10 } In turn, triggering events is only interesting if something is listening for the event. Listeners attach to the EventManager, specifying a named event and the callback to notify. The callback receives an Event object, which has accessors for retrieving the event name, context, and parameters. Let’s add a listener, and trigger the event. 1 use ZendLogFactory as LogFactory; 2 3 4 5 6 7 8 $log = LogFactory($someConfig); $foo = new Foo(); $foo->getEventManager()->attach(’bar’, function ($e) use ($log) { $event = $e->getName(); $target = get_class($e->getTarget()); $params = json_encode($e->getParams()); 9 $log->info(sprintf( ’%s called on %s, using params %s’, $event, $target, $params )); 10 11 12 13 14 15 16 }); 17 18 19 20 // Results in log message: $foo->bar(’baz’, ’bat’); // reading: bar called on Foo, using params {"baz" : "baz", "bat" : "bat"}" Note that the second argument to attach() is any valid callback; an anonymous function is shown in the example in order to keep the example self-contained. However, you could also utilize a valid function name, a functor, a string referencing a static method, or an array callback with a named static method or instance method. Again, any PHP callback is valid. Sometimes you may want to specify listeners without yet having an object instance of the class composing an EventManager. Zend Framework enables this through the concept of a SharedEventCollection. Simply put, you can inject individual EventManager instances with a well-known SharedEventCollection, and the EventManager instance will query it for additional listeners. Listeners attach to a SharedEventCollection in roughly the same way the do normal event managers; the call to attach is identical to the EventManager, 392 Chapter 94. The EventManager
  • 431. Zend Framework 2 Documentation, Release 2.2.5 but expects an additional parameter at the beginning: a named instance. Remember the example of composing an EventManager, how we passed it __CLASS__? That value, or any strings you provide in an array to the constructor, may be used to identify an instance when using a SharedEventCollection. As an example, assuming we have a SharedEventManager instance that we know has been injected in our EventManager instances (for instance, via dependency injection), we could change the above example to attach via the shared collection: 1 use ZendLogFactory as LogFactory; 2 3 // Assume $events is a ZendEventManagerSharedEventManager instance 4 5 6 7 8 9 $log = LogFactory($someConfig); $events->attach(’Foo’, ’bar’, function ($e) use ($log) { $event = $e->getName(); $target = get_class($e->getTarget()); $params = json_encode($e->getParams()); 10 $log->info(sprintf( ’%s called on %s, using params %s’, $event, $target, $params )); 11 12 13 14 15 16 17 }); 18 19 20 21 // Later, instantiate Foo: $foo = new Foo(); $foo->getEventManager()->setSharedManager($events); 22 23 24 25 26 // And we can still trigger the above event: $foo->bar(’baz’, ’bat’); // results in log message: // bar called on Foo, using params {"baz" : "baz", "bat" : "bat"}" Note: StaticEventManager As of 2.0.0beta3, you can use the StaticEventManager singleton as a SharedEventCollection. As such, you do not need to worry about where and how to get access to the SharedEventCollection; it’s globally available by simply calling StaticEventManager::getInstance(). Be aware, however, that its usage is deprecated within the framework, and starting with 2.0.0beta4, you will instead configure a SharedEventManager instance that will be injected by the framework into individual EventManager instances. The EventManager also provides the ability to detach listeners, short-circuit execution of an event either from within a listener or by testing return values of listeners, test and loop through the results returned by listeners, prioritize listeners, and more. Many of these features are detailed in the examples. 94.2.1 Wildcard Listeners Sometimes you’ll want to attach the same listener to many events or to all events of a given instance – or potentially, with a shared event collection, many contexts, and many events. The EventManager component allows for this. 94.2. Quick Start 393
  • 432. Zend Framework 2 Documentation, Release 2.2.5 Attaching to many events at once 1 2 $events = new EventManager(); $events->attach(array(’these’, ’are’, ’event’, ’names’), $callback); Note that if you specify a priority, that priority will be used for all events specified. Attaching using the wildcard 1 2 $events = new EventManager(); $events->attach(’*’, $callback); Note that if you specify a priority, that priority will be used for this listener for any event triggered. What the above specifies is that any event triggered will result in notification of this particular listener. Attaching to many events at once via a SharedEventManager 1 2 3 $events = new SharedEventManager(); // Attach to many events on the context "foo" $events->attach(’foo’, array(’these’, ’are’, ’event’, ’names’), $callback); 4 5 6 // Attach to many events on the contexts "foo" and "bar" $events->attach(array(’foo’, ’bar’), array(’these’, ’are’, ’event’, ’names’), $callback); Note that if you specify a priority, that priority will be used for all events specified. Attaching using the wildcard via a SharedEventManager 1 2 3 $events = new SharedEventManager(); // Attach to all events on the context "foo" $events->attach(’foo’, ’*’, $callback); 4 5 6 // Attach to all events on the contexts "foo" and "bar" $events->attach(array(’foo’, ’bar’), ’*’, $callback); Note that if you specify a priority, that priority will be used for all events specified. The above is specifying that for the contexts “foo” and “bar”, the specified listener should be notified for any event they trigger. 94.3 Configuration Options EventManager Options identifier A string or array of strings to which the given EventManager instance can answer when accessed via a SharedEventManager. event_class The name of an alternate Event class to use for representing events passed to listeners. shared_collections An instance of a SharedEventCollection instance to use when triggering events. 394 Chapter 94. The EventManager
  • 433. Zend Framework 2 Documentation, Release 2.2.5 94.4 Available Methods __construct __construct(null|string|int $identifier) Constructs a new EventManager instance, using the given identifier, if provided, for purposes of shared collections. setEventClass setEventClass(string $class) Provide the name of an alternate Event class to use when creating events to pass to triggered listeners. setSharedCollections setSharedCollections(SharedEventCollection $collections = null) An instance of a SharedEventCollection instance to use when triggering events. getSharedCollections getSharedCollections() Returns the currently attached SharedEventCollection instance. Returns either a null if no collection is attached, or a SharedEventCollection instance otherwise. trigger trigger(string $event, mixed $target = null, mixed $argv, callback $callback = null) Triggers all listeners to a named event. The recommendation is to use the current function/method name for $event, appending it with values such as ”.pre”, ”.post”, etc. as needed. $target should be the current object instance, or the name of the function if not triggering within an object. $argv should typically be an associative array or ArrayAccess instance; we recommend using the parameters passed to the function/method (compact() is often useful here). This method can also take a callback and behave in the same way as triggerUntil(). The method returns an instance of ResponseCollection, which may be used to introspect return values of the various listeners, test for short-circuiting, and more. triggerUntil triggerUntil(string $event, mixed $target, mixed $argv = null, callback $callback = null) Triggers all listeners to a named event, just like trigger(), with the addition that it passes the return value from each listener to $callback; if $callback returns a boolean true value, execution of the listeners is interrupted. You can test for this using $result->stopped(). attach attach(string $event, callback $callback, int $priority) Attaches $callback to the EventManager instance, listening for the event $event. If a $priority is provided, the listener will be inserted into the internal listener stack using that priority; higher values execute earliest. (Default priority is “1”, and negative priorities are allowed.) The method returns an instance of ZendStdlibCallbackHandler; this value can later be passed to detach() if desired. attachAggregate attachAggregate(string|ListenerAggregate $aggregate) If a string is passed for $aggregate, instantiates that class. The $aggregate is then passed the EventManager instance to its attach() method so that it may register listeners. The ListenerAggregate instance is returned. detach detach(CallbackHandler|ListenerAggregateInterface $listener) Scans all listeners, and detaches any that match $listener so that they will no longer be triggered. Returns a boolean true if any listeners have been identified and unsubscribed, and a boolean false otherwise. 94.4. Available Methods 395
  • 434. Zend Framework 2 Documentation, Release 2.2.5 detachAggregate detachAggregate(ListenerAggregateInterface $aggregate) Loops through all listeners of all events to identify listeners that are represented by the aggregate; for all matches, the listeners will be removed. Returns a boolean true if any listeners have been identified and unsubscribed, and a boolean false otherwise. getEvents getEvents() Returns an array of all event names that have listeners attached. getListeners getListeners(string $event) Returns a ZendStdlibPriorityQueue instance of all listeners attached to $event. clearListeners clearListeners(string $event) Removes all listeners attached to $event. prepareArgs prepareArgs(array $args) Creates an ArrayObject from the provided $args. This can be useful if you want yours listeners to be able to modify arguments such that later listeners or the triggering method can see the changes. 94.5 Examples Modifying Arguments Occasionally it can be useful to allow listeners to modify the arguments they receive so that later listeners or the calling method will receive those changed values. As an example, you might want to pre-filter a date that you know will arrive as a string and convert it to a DateTime argument. To do this, you can pass your arguments to prepareArgs(), and pass this new object when triggering an event. You will then pull that value back into your method. 1 2 3 class ValueObject { // assume a composed event manager 4 function inject(array $values) { $argv = compact(’values’); $argv = $this->getEventManager()->prepareArgs($argv); $this->getEventManager()->trigger(__FUNCTION__, $this, $argv); $date = isset($argv[’values’][’date’]) ? $argv[’values’][’date’] : new DateTime(’now’); 5 6 7 8 9 10 11 // ... 12 } 13 14 } 15 16 $v = new ValueObject(); 17 18 19 20 21 22 23 $v->getEventManager()->attach(’inject’, function($e) { $values = $e->getParam(’values’); if (!$values) { return; } if (!isset($values[’date’])) { 396 Chapter 94. The EventManager
  • 435. Zend Framework 2 Documentation, Release 2.2.5 $values[’date’] = new DateTime(’now’); return; 24 25 } $values[’date’] = new Datetime($values[’date’]); 26 27 28 }); 29 30 31 32 $v->inject(array( ’date’ => ’2011-08-10 15:30:29’, )); Short Circuiting One common use case for events is to trigger listeners until either one indicates no further processing should be done, or until a return value meets specific criteria. As examples, if an event creates a Response object, it may want execution to stop. 1 2 $listener = function($e) { // do some work 3 // Stop propagation and return a response $e->stopPropagation(true); return $response; 4 5 6 7 }; Alternately, we could do the check from the method triggering the event. 1 2 3 class Foo implements DispatchableInterface { // assume composed event manager 4 public function dispatch(Request $request, Response $response = null) { $argv = compact(’request’, ’response’); $results = $this->getEventManager()->triggerUntil(__FUNCTION__, $this, $argv, function($v) { return ($v instanceof Response); }); } 5 6 7 8 9 10 11 12 } Typically, you may want to return a value that stopped execution, or use it some way. Both trigger() and triggerUntil() return a ResponseCollection instance; call its stopped() method to test if execution was stopped, and last() method to retrieve the return value from the last executed listener: 1 2 3 class Foo implements DispatchableInterface { // assume composed event manager 4 5 6 7 8 9 10 public function dispatch(Request $request, Response $response = null) { $argv = compact(’request’, ’response’); $results = $this->getEventManager()->triggerUntil(__FUNCTION__, $this, $argv, function($v) { return ($v instanceof Response); }); 11 12 13 14 // Test if execution was halted, and return last result: if ($results->stopped()) { return $results->last(); 94.5. Examples 397
  • 436. Zend Framework 2 Documentation, Release 2.2.5 } 15 16 // continue... 17 } 18 19 } Assigning Priority to Listeners One use case for the EventManager is for implementing caching systems. As such, you often want to check the cache early, and save to it late. The third argument to attach() is a priority value. The higher this number, the earlier that listener will execute; the lower it is, the later it executes. The value defaults to 1, and values will trigger in the order registered within a given priority. So, to implement a caching system, our method will need to trigger an event at method start as well as at method end. At method start, we want an event that will trigger early; at method end, an event should trigger late. Here is the class in which we want caching: 1 2 3 class SomeValueObject { // assume it composes an event manager 4 public function get($id) { $params = compact(’id’); $results = $this->getEventManager()->trigger(’get.pre’, $this, $params); 5 6 7 8 9 // If an event stopped propagation, return the value if ($results->stopped()) { return $results->last(); } 10 11 12 13 14 // do some work... 15 16 $params[’__RESULT__’] = $someComputedContent; $this->getEventManager()->trigger(’get.post’, $this, $params); 17 18 } 19 20 } Now, let’s create a ListenerAggregateInterface that can handle caching for us: 1 2 3 4 use use use use ZendCacheCache; ZendEventManagerEventManagerInterface; ZendEventManagerListenerAggregateInterface; ZendEventManagerEventInterface; 5 6 7 8 class CacheListener implements ListenerAggregateInterface { protected $cache; 9 protected $listeners = array(); 10 11 public function __construct(Cache $cache) { $this->cache = $cache; } 12 13 14 15 398 Chapter 94. The EventManager
  • 437. Zend Framework 2 Documentation, Release 2.2.5 16 public function attach(EventManagerInterface $events) { $this->listeners[] = $events->attach(’get.pre’, array($this, ’load’), 100); $this->listeners[] = $events->attach(’get.post’, array($this, ’save’), -100); } 17 18 19 20 21 22 public function detach(EventManagerInterface $events) { foreach ($this->listeners as $index => $listener) { if ($events->detach($listener)) { unset($this->listeners[$index]); } } } 23 24 25 26 27 28 29 30 31 public function load(EventInterface $e) { $id = get_class($e->getTarget()) . ’-’ . json_encode($e->getParams()); if (false !== ($content = $this->cache->load($id))) { $e->stopPropagation(true); return $content; } } 32 33 34 35 36 37 38 39 40 public function save(EventInterface $e) { $params = $e->getParams(); $content = $params[’__RESULT__’]; unset($params[’__RESULT__’]); 41 42 43 44 45 46 $id = get_class($e->getTarget()) . ’-’ . json_encode($params); $this->cache->save($content, $id); 47 48 } 49 50 } We can then attach the aggregate to an instance. 1 2 3 $value = new SomeValueObject(); $cacheListener = new CacheListener($cache); $value->getEventManager()->attachAggregate($cacheListener); Now, as we call get(), if we have a cached entry, it will be returned immediately; if not, a computed entry will be cached when we complete the method. 94.5. Examples 399
  • 438. Zend Framework 2 Documentation, Release 2.2.5 400 Chapter 94. The EventManager
  • 439. CHAPTER 95 Introduction to ZendFeed ZendFeed provides functionality for consuming RSS and Atom feeds. It provides a natural syntax for accessing elements of feeds, feed attributes, and entry attributes. ZendFeed also has extensive support for modifying feed and entry structure with the same natural syntax, and turning the result back into XML. In the future, this modification support could provide support for the Atom Publishing Protocol. ZendFeed consists of ZendFeedReader for reading RSS and Atom feeds, ZendFeedWriter for writing RSS and Atom feeds, and ZendFeedPubSubHubbub for working with Hub servers. Furthermore, both ZendFeedReader and ZendFeedWriter support extensions which allows for working with additional data in feeds, not covered in the core API but used in conjunction with RSS and Atom feeds. In the example below, we demonstrate a simple use case of retrieving an RSS feed and saving relevant portions of the feed data to a simple PHP array, which could then be used for printing the data, storing to a database, etc. Note: Be aware Many RSS feeds have different channel and item properties available. The RSS specification provides for many optional properties, so be aware of this when writing code to work with RSS data. ZendFeed supports all optional properties of the core RSS and Atom specifications. 95.1 Reading RSS Feed Data with ZendFeedReader 1 2 3 4 5 6 7 8 9 // Fetch the latest Slashdot headlines try { $slashdotRss = ZendFeedReaderReader::import(’http://rss.slashdot.org/Slashdot/slashdot’); } catch (ZendFeedReaderExceptionRuntimeException $e) { // feed import failed echo "Exception caught importing feed: {$e->getMessage()}n"; exit; } 10 11 12 13 14 15 // Initialize the $channel = array( ’title’ ’link’ ’description’ channel/feed data array => $slashdotRss->getTitle(), => $slashdotRss->getLink(), => $slashdotRss->getDescription(), 401
  • 440. Zend Framework 2 Documentation, Release 2.2.5 ’items’ ); 16 17 => array() 18 19 20 21 22 23 24 25 26 // Loop over each channel item/entry and store relevant data for each foreach ($slashdotRss as $item) { $channel[’items’][] = array( ’title’ => $item->getTitle(), ’link’ => $item->getLink(), ’description’ => $item->getDescription() ); } Your $channel array now contains the basic meta-information for the RSS channel and all items that it contained. The process is identical for Atom feeds since ZendFeed features a common denominator API, i.e. all getters and setters are the same regardless of feed format. 402 Chapter 95. Introduction to ZendFeed
  • 441. CHAPTER 96 Importing Feeds ZendFeed enables developers to retrieve feeds very easily, by using ZendFeaderReader. If you know the URI of a feed, simply use the ZendFeedReaderReader::import() method: 1 $feed = ZendFeedReaderReader::import(’http://feeds.example.com/feedName’); You can also use ZendFeedReaderReader to fetch the contents of a feed from a file or the contents of a PHP string variable: 1 2 // importing a feed from a text file $feedFromFile = ZendFeedReaderReader::importFile(’feed.xml’); 3 4 5 // importing a feed from a PHP string variable $feedFromPHP = ZendFeedReaderReader::importString($feedString); In each of the examples above, an object of a class that extends ZendFeedReaderFeedAbstractFeed is returned upon success, depending on the type of the feed. If an RSS feed were retrieved via one of the import methods above, then a ZendFeedReaderFeedRss object would be returned. On the other hand, if an Atom feed were imported, then a ZendFeedReaderFeedAtom object is returned. The import methods will also throw a ZendFeedExceptionReaderRuntimeException object upon failure, such as an unreadable or malformed feed. 96.1 Dumping the contents of a feed To dump the contents of a ZendFeedReaderFeedAbstractFeed instance, you may use the saveXml() method. 1 assert($feed instanceof ZendFeedReaderFeedAbstractFeed); 2 3 4 // dump the feed to standard output print $feed->saveXml(); 403
  • 442. Zend Framework 2 Documentation, Release 2.2.5 404 Chapter 96. Importing Feeds
  • 443. CHAPTER 97 Retrieving Feeds from Web Pages 97.1 Find Feed Links Web pages often contain <link> tags that refer to feeds with content relevant to the particular page. ZendFeedReaderReader enables you to retrieve all feeds referenced by a web page with one simple method call: 1 $feedLinks = ZendFeedReaderReader::findFeedLinks(’http://www.example.com/news.html’); Here the findFeedLinks() method returns a ZendFeedReaderFeedSet object, that is in turn, a collection of other ZendFeedReaderFeedSet objects, that are referenced by <link> tags on the news.html web page. ZendFeedReaderReader will throw a ZendFeedReaderExceptionRuntimeException upon failure, such as an HTTP 404 response code or a malformed feed. You can examine all feed links located by iterating across the collection: 1 2 3 4 5 6 7 $rssFeed = null; $feedLinks = ZendFeedReaderReader::findFeedLinks(’http://www.example.com/news.html’); foreach ($feedLinks as $link) { if (stripos($link[’type’], ’application/rss+xml’) !== false) { $rssFeed = $link[’href’]; break; } Each ZendFeedReaderFeedSet object will expose the rel, href, type and title properties of detected links for all RSS, Atom or RDF feeds. You can always select the first encountered link of each type by using a shortcut: 1 2 3 $rssFeed = null; $feedLinks = ZendFeedReaderReader::findFeedLinks(’http://www.example.com/news.html’); $firstAtomFeed = $feedLinks->atom; 405
  • 444. Zend Framework 2 Documentation, Release 2.2.5 406 Chapter 97. Retrieving Feeds from Web Pages
  • 445. CHAPTER 98 Consuming an RSS Feed 98.1 Reading a feed Reading an RSS feed is as simple as passing the URL of the feed to ZendFeedReaderReader‘s import method. 1 $channel = ZendFeedReaderReader::import(’http://rss.example.com/channelName’); If any errors occur fetching the feed, a ZendFeedReaderExceptionRuntimeException will be thrown. 98.2 Get properties Once you have a feed object, you can access any of the standard RSS “channel” properties directly on the object: 1 echo $channel->getTitle(); Properties of the channel can be accessed via getter methods, such as getTitle, getAuthor ... If channel properties have attributes, the getter method will return a key/value pair, where the key is the attribute name, and the value is the attribute value. 1 2 $author = $channel->getAuthor(); echo $author[’name’]; Most commonly you’ll want to loop through the feed and do something with its entries. ZendFeedReaderFeedRss internally converts all entries to a ZendFeedReaderEntryRss. Entry properties, similarly to channel properties, can be accessed via getter methods, such as getTitle, getDescription ... An example of printing all titles of articles in a channel is: 1 2 3 foreach ($channel as $item) { echo $item->getTitle() . "n"; } If you are not familiar with RSS, here are the standard elements you can expect to be available in an RSS channel and in individual RSS items (entries). 407
  • 446. Zend Framework 2 Documentation, Release 2.2.5 Required channel elements: • title- The name of the channel • link- The URL of the web site corresponding to the channel • description- A sentence or several describing the channel Common optional channel elements: • pubDate- The publication date of this set of content, in RFC 822 date format • language- The language the channel is written in • category- One or more (specified by multiple tags) categories the channel belongs to RSS <item> elements do not have any strictly required elements. However, either title or description must be present. Common item elements: • title- The title of the item • link- The URL of the item • description- A synopsis of the item • author- The author’s email address • category- One more categories that the item belongs to • comments-URL of comments relating to this item • pubDate- The date the item was published, in RFC 822 date format In your code you can always test to see if an element is non-empty with: 1 2 3 if ($item->getPropname()) { // ... proceed. } Where relevant, ZendFeed supports a number of common RSS extensions including Dublin Core, Atom (inside RSS) and the Content, Slash, Syndication, Syndication/Thread and several other extensions or modules. For further information, the official RSS 2.0 specification is available at: http://blogs.law.harvard.edu/tech/rss 408 Chapter 98. Consuming an RSS Feed
  • 447. CHAPTER 99 Consuming an Atom Feed ZendFeedReaderFeedAtom is used in much the same way as ZendFeedReaderFeedRss. It provides the same access to feed-level properties and iteration over entries in the feed. The main difference is in the structure of the Atom protocol itself. Atom is a successor to RSS; it is a more generalized protocol and it is designed to deal more easily with feeds that provide their full content inside the feed, splitting RSS‘ description tag into two elements, summary and content, for that purpose. 99.1 Basic Use of an Atom Feed Read an Atom feed and print the title and summary of each entry: 1 2 3 4 5 6 7 $feed = ZendFeedReaderReader::import(’http://atom.example.com/feed/’); echo ’The feed contains ’ . $feed->count() . ’ entries.’ . "nn"; foreach ($feed as $entry) { echo ’Title: ’ . $entry->getTitle() . "n"; echo ’Description: ’ . $entry->getDescription() . "n"; echo ’URL: ’ . $entry->getLink() . "nn"; } In an Atom feed you can expect to find the following feed properties: • title- The feed’s title, same as RSS‘s channel title • id- Every feed and entry in Atom has a unique identifier • link- Feeds can have multiple links, which are distinguished by a type attribute The equivalent to RSS‘s channel link would be type="text/html". if the link is to an alternate version of the same content that’s in the feed, it would have a rel="alternate" attribute. • subtitle- The feed’s description, equivalent to RSS‘ channel description • author- The feed’s author, with name and email sub-tags Atom entries commonly have the following properties: • id- The entry’s unique identifier • title- The entry’s title, same as RSS item titles • link- A link to another format or an alternate view of this entry 409
  • 448. Zend Framework 2 Documentation, Release 2.2.5 The link property of an atom entry typically has an href attribute. • summary- A summary of this entry’s content • content- The full content of the entry; can be skipped if the feed just contains summaries • author- with name and email sub-tags like feeds have • published- the date the entry was published, in RFC 3339 format • updated- the date the entry was last updated, in RFC 3339 format Where relevant, ZendFeed supports a number of common RSS extensions including Dublin Core and the Content, Slash, Syndication, Syndication/Thread and several other extensions in common use on blogs. For more information on Atom and plenty of resources, see http://www.atomenabled.org/. 410 Chapter 99. Consuming an Atom Feed
  • 449. CHAPTER 100 Consuming a Single Atom Entry Single Atom <entry> elements are also valid by themselves. Usually the URL for an entry is the feed’s URL followed by /<entryId>, such as http://atom.example.com/feed/1, using the example URL we used above. This pattern may exist for some web services which use Atom as a container syntax. If you read a single entry, you will have a ZendFeedReaderEntryAtom object. 100.1 Reading a Single-Entry Atom Feed 1 2 $entry = ZendFeedReaderReader::import(’http://atom.example.com/feed/1’); echo ’Entry title: ’ . $entry->getTitle(); 411
  • 450. Zend Framework 2 Documentation, Release 2.2.5 412 Chapter 100. Consuming a Single Atom Entry
  • 451. CHAPTER 101 ZendFeed and Security 101.1 Introduction As with any data coming from a source that is beyond the developer’s control, special attention needs to be given to securing, validating and filtering that data. Similar to data input to our application by users, data coming from RSS and Atom feeds should also be considered unsafe and potentially dangerous, as it allows the delivery of HTML and xHTML 1 . Because data validation and filtration is out of ZendFeed‘s scope, this task is left for implementation by the developer, by using libraries such as ZendEscaper for escaping and HTMLPurifier for validating and filtering feed data. Escaping and filtering of potentially insecure data is highly recommended before outputting it anywhere in our application or before storing that data in some storage engine (be it a simple file, a database...). 101.2 Filtering data using HTMLPurifier Currently the best available library for filtering and validating (x)HTML data in PHP is HTMLPurifier and, as such, is the recommended tool for this task. HTMLPurifier works by filtering out all (x)HTML from the data, except for the tags and attributes specifically allowed in a whitelist, and by checking and fixing nesting of tags, ensuring a standardscompliant output. The following examples will show a basic usage of HTMLPurifier, but developers are urged to go through and read HTMLPurifier’s documentation. 1 2 3 4 5 6 7 8 9 10 11 12 // Setting HTMLPurifier’s options $options = array( // Allow only paragraph tags // and anchor tags wit the href attribute array( ’HTML.Allowed’, ’p,a[href]’ ), // Format end output with Tidy array( ’Output.TidyFormat’, true 1 http://tools.ietf.org/html/rfc4287#section-8.1 413
  • 452. Zend Framework 2 Documentation, Release 2.2.5 ), // Assume XHTML 1.0 Strict Doctype array( ’HTML.Doctype’, ’XHTML 1.0 Strict’ ), // Disable cache, but see note after the example array( ’Cache.DefinitionImpl’, null ) 13 14 15 16 17 18 19 20 21 22 23 24 ); 25 26 27 28 29 30 // Configuring HTMLPurifier $config = HTMLPurifier_Config::createDefault(); foreach ($options as $option) { $config->set($option[0], $option[1]); } 31 32 33 // Creating a HTMLPurifier with it’s config $purifier = new HTMLPurifier($config); 34 35 36 37 38 39 40 41 42 // Fetch the RSS try { $rss = ZendFeedReaderReader::import(’http://www.planet-php.net/rss/’); } catch (ZendFeedExceptionReaderRuntimeException $e) { // feed import failed echo "Exception caught importing feed: {$e->getMessage()}n"; exit; } 43 44 45 46 47 48 49 50 51 // Initialize the channel data array // See that we’re cleaning the description with HTMLPurifier $channel = array( ’title’ => $rss->getTitle(), ’link’ => $rss->getLink(), ’description’ => $purifier->purify($rss->getDescription()), ’items’ => array() ); 52 53 54 55 56 57 58 59 60 61 // Loop over each channel item and store relevant data // See that we’re cleaning the descriptions with HTMLPurifier foreach ($rss as $item) { $channel[’items’][] = array( ’title’ => $item->getTitle(), ’link’ => $item->getLink(), ’description’ => $purifier->purify($item->getDescription()) ); } Note: HTMLPurifier is using the PHP Tidy extension to clean and repair the final output. If this extension is not available, it will silently fail but its availability has no impact on the library’s security. Note: For the sake of this example, the HTMLPurifier’s cache is disabled, but it is recommended to configure caching and use its standalone include file as it can improve the performance of HTMLPurifier substantially. 414 Chapter 101. ZendFeed and Security
  • 453. Zend Framework 2 Documentation, Release 2.2.5 101.3 Escaping data using ZendEscaper To help prevent XSS attacks, Zend Framework has a new component ZendEscaper, which complies to the current OWASP recommendations, and as such, is the recommended tool for escaping HTML tags and attributes, Javascript, CSS and URLs before outputing any potentially insecure data to the users. 1 2 3 4 5 6 7 try { $rss = ZendFeedReaderReader::import(’http://www.planet-php.net/rss/’); } catch (ZendFeedExceptionReaderRuntimeException $e) { // feed import failed echo "Exception caught importing feed: {$e->getMessage()}n"; exit; } 8 9 10 11 12 13 14 // Validate all URIs $linkValidator = new ZendValidatorUri; $link = null; if ($linkValidator->isValid($rss->getLink())) { $link = $rss->getLink(); } 15 16 17 // Escaper used for escaping data $escaper = new ZendEscaperEscaper(’utf-8’); 18 19 20 21 22 23 24 25 // Initialize the $channel = array( ’title’ ’link’ ’description’ ’items’ ); channel data array => => => => $escaper->escapeHtml($rss->getTitle()), $escaper->escapeHtml($link), $escaper->escapeHtml($rss->getDescription()), array() 26 27 28 29 30 31 32 33 34 35 36 37 38 // Loop over each channel item and store relevant data foreach ($rss as $item) { $link = null; if ($linkValidator->isValid($rss->getLink())) { $link = $item->getLink(); } $channel[’items’][] = array( ’title’ => $escaper->escapeHtml($item->getTitle()), ’link’ => $escaper->escapeHtml($link), ’description’ => $escaper->escapeHtml($item->getDescription()) ); } The feed data is now safe for output to HTML templates. You can, of course, skip escaping when simply storing the data persistently but remember to escape it on output later! Of course, these are just basic examples, and cannot cover all possible scenarios that you, as a developer, can, and most likely will, encounter. Your responsibility is to learn what libraries and tools are at your disposal, and when and how to use them to secure your web applications. 101.3. Escaping data using ZendEscaper 415
  • 454. Zend Framework 2 Documentation, Release 2.2.5 416 Chapter 101. ZendFeed and Security
  • 455. CHAPTER 102 ZendFeedReaderReader 102.1 Introduction ZendFeedReaderReader is a component used to consume RSS and Atom feeds of any version, including RDF/RSS 1.0, RSS 2.0, Atom 0.3 and Atom 1.0. The API for retrieving feed data is deliberately simple since ZendFeedReader is capable of searching any feed of any type for the information requested through the API. If the typical elements containing this information are not present, it will adapt and fall back on a variety of alternative elements instead. This ability to choose from alternatives removes the need for users to create their own abstraction layer on top of the component to make it useful or have any in-depth knowledge of the underlying standards, current alternatives, and namespaced extensions. Internally, ZendFeedReaderReader works almost entirely on the basis of making XPath queries against the feed XML‘s Document Object Model. This singular approach to parsing is consistent and the component offers a plugin system to add to the Feed and Entry level API by writing Extensions on a similar basis. Performance is assisted in three ways. First of all, ZendFeedReaderReader supports caching using ZendCache to maintain a copy of the original feed XML. This allows you to skip network requests for a feed URI if the cache is valid. Second, the Feed and Entry level API is backed by an internal cache (non-persistent) so repeat API calls for the same feed will avoid additional DOM or XPath use. Thirdly, importing feeds from a URI can take advantage of HTTP Conditional GET requests which allow servers to issue an empty 304 response when the requested feed has not changed since the last time you requested it. In the final case, an instance of ZendCache will hold the last received feed along with the ETag and Last-Modified header values sent in the HTTP response. ZendFeedReaderReader is not capable of constructing feeds and delegates this responsibility to ZendFeedWriterWriter. 102.2 Importing Feeds Feeds can be imported from a string, file or an URI. Importing from a URI can additionally utilise a HTTP Conditional GET request. If importing fails, an exception will be raised. The end result will be an object of type ZendFeedReaderFeedAbstractFeed, the core implementations of which are ZendFeedReaderFeedRss and ZendFeedReaderFeedAtom. Both objects support multiple (all existing) versions of these broad feed types. In the following example, we import an RDF/RSS 1.0 feed and extract some basic information that can be saved to a database or elsewhere. 417
  • 456. Zend Framework 2 Documentation, Release 2.2.5 1 2 3 4 5 6 7 8 9 $feed = ZendFeedReaderReader::import(’http://www.planet-php.net/rdf/’); $data = array( ’title’ => $feed->getTitle(), ’link’ => $feed->getLink(), ’dateModified’ => $feed->getDateModified(), ’description’ => $feed->getDescription(), ’language’ => $feed->getLanguage(), ’entries’ => array(), ); 10 11 12 13 14 15 16 17 18 19 20 21 foreach ($feed as $entry) { $edata = array( ’title’ => $entry->getTitle(), ’description’ => $entry->getDescription(), ’dateModified’ => $entry->getDateModified(), ’authors’ => $entry->getAuthors(), ’link’ => $entry->getLink(), ’content’ => $entry->getContent() ); $data[’entries’][] = $edata; } The example above demonstrates ZendFeedReaderReader‘s API, and it also demonstrates some of its internal operation. In reality, the RDF feed selected does not have any native date or author elements, however it does utilise the Dublin Core 1.1 module which offers namespaced creator and date elements. ZendFeedReaderReader falls back on these and similar options if no relevant native elements exist. If it absolutely cannot find an alternative it will return NULL, indicating the information could not be found in the feed. You should note that classes implementing ZendFeedReaderFeedAbstractFeed also implement the SPL Iterator and Countable interfaces. Feeds can also be imported from strings or files. 1 2 // from a URI $feed = ZendFeedReaderReader::import(’http://www.planet-php.net/rdf/’); 3 4 5 // from a String $feed = ZendFeedReaderReader::importString($feedXmlString); 6 7 8 // from a file $feed = ZendFeedReaderReader::importFile(’./feed.xml’); 102.3 Retrieving Underlying Feed and Entry Sources ZendFeedReaderReader does its best not to stick you in a narrow confine. If you need to work on a feed outside of ZendFeedReaderReader, you can extract the base DOMDocument or DOMElement objects from any class, or even an XML string containing these. Also provided are methods to extract the current DOMXPath object (with all core and Extension namespaces registered) and the correct prefix used in all XPath queries for the current Feed or Entry. The basic methods to use (on any object) are saveXml(), getDomDocument(), getElement(), getXpath() and getXpathPrefix(). These will let you break free of ZendFeedReader and do whatever else you want. • saveXml() returns an XML string containing only the element representing the current object. • getDomDocument() returns the DOMDocument object representing the entire feed (even if called from an Entry object). • getElement() returns the DOMElement of the current object (i.e. the Feed or current Entry). 418 Chapter 102. ZendFeedReaderReader
  • 457. Zend Framework 2 Documentation, Release 2.2.5 • getXpath() returns the DOMXPath object for the current feed (even if called from an Entry object) with the namespaces of the current feed type and all loaded Extensions pre-registered. • getXpathPrefix() returns the query prefix for the current object (i.e. the Feed or current Entry) which includes the correct XPath query path for that specific Feed or Entry. Here’s an example where a feed might include an RSS Extension not supported by ZendFeedReaderReader out of the box. Notably, you could write and register an Extension (covered later) to do this, but that’s not always warranted for a quick check. You must register any new namespaces on the DOMXPath object before use unless they are registered by ZendFeedReader or an Extension beforehand. 1 2 3 4 5 6 7 $feed = ZendFeedReaderReader::import(’http://www.planet-php.net/rdf/’); $xpathPrefix = $feed->getXpathPrefix(); $xpath = $feed->getXpath(); $xpath->registerNamespace(’admin’, ’http://webns.net/mvcb/’); $reportErrorsTo = $xpath->evaluate(’string(’ . $xpathPrefix . ’/admin:errorReportsTo)’); Warning: If you register an already registered namespace with a different prefix name to that used internally by ZendFeedReaderReader, it will break the internal operation of this component. 102.4 Cache Support and Intelligent Requests 102.4.1 Adding Cache Support to ZendFeedReaderReader ZendFeedReaderReader supports using an instance of ZendCache to cache feeds (as XML) to avoid unnecessary network requests. Adding a cache is as simple here as it is for other Zend Framework components, create and configure your cache and then tell ZendFeedReaderReader to use it! The cache key used is “ZendFeedReader” followed by the MD5 hash of the feed’s URI. 1 $cache = ZendCacheStorageFactory::adapterFactory(’Memory’); 2 3 ZendFeedReaderReader::setCache($cache); 102.4.2 HTTP Conditional GET Support The big question often asked when importing a feed frequently, is if it has even changed. With a cache enabled, you can add HTTP Conditional GET support to your arsenal to answer that question. Using this method, you can request feeds from URIs and include their last known ETag and Last-Modified response header values with the request (using the If-None-Match and If-Modified-Since headers). If the feed on the server remains unchanged, you should receive a 304 response which tells ZendFeedReaderReader to use the cached version. If a full feed is sent in a response with a status code of 200, this means the feed has changed and ZendFeedReaderReader will parse the new version and save it to the cache. It will also cache the new ETag and Last-Modified header values for future use. These “conditional” requests are not guaranteed to be supported by the server you request a URI of, but can be attempted regardless. Most common feed sources like blogs should however have this supported. To enable conditional requests, you will need to provide a cache to ZendFeedReaderReader. 1 $cache = ZendCacheStorageFactory::adapterFactory(’Memory’); 2 3 ZendFeedReaderReader::setCache($cache); 102.4. Cache Support and Intelligent Requests 419
  • 458. Zend Framework 2 Documentation, Release 2.2.5 4 ZendFeedReaderReader::useHttpConditionalGet(); 5 6 $feed = ZendFeedReaderReader::import(’http://www.planet-php.net/rdf/’); In the example above, with HTTP Conditional GET requests enabled, the response header values for ETag and LastModified will be cached along with the feed. For the the cache’s lifetime, feeds will only be updated on the cache if a non-304 response is received containing a valid RSS or Atom XML document. If you intend on managing request headers from outside ZendFeedReaderReader, you can set the relevant If-None-Matches and If-Modified-Since request headers via the URI import method. 1 2 3 4 5 $lastEtagReceived = ’5e6cefe7df5a7e95c8b1ba1a2ccaff3d’; $lastModifiedDateReceived = ’Wed, 08 Jul 2009 13:37:22 GMT’; $feed = ZendFeedReaderReader::import( $uri, $lastEtagReceived, $lastModifiedDateReceived ); 102.5 Locating Feed URIs from Websites These days, many websites are aware that the location of their XML feeds is not always obvious. A small RDF, RSS or Atom graphic helps when the user is reading the page, but what about when a machine visits trying to identify where your feeds are located? To assist in this, websites may point to their feeds using <link> tags in the <head> section of their HTML. To take advantage of this, you can use ZendFeedReaderReader to locate these feeds using the static findFeedLinks() method. This method calls any URI and searches for the location of RSS, RDF and Atom feeds assuming the website’s HTML contains the relevant links. It then returns a value object where you can check for the existence of a RSS, RDF or Atom feed URI. The returned object is an ArrayObject subclass called ZendFeedReaderFeedSet so you can cast it to an array, or iterate over it, to access all the detected links. However, as a simple shortcut, you can just grab the first RSS, RDF or Atom link using its public properties as in the example below. Otherwise, each element of the ArrayObject is a simple array with the keys “type” and “uri” where the type is one of “rdf”, “rss” or “atom”. 1 $links = ZendFeedReaderReader::findFeedLinks(’http://www.planet-php.net’); 2 3 4 5 6 7 8 9 10 11 if (isset($links->rdf)) { echo $links->rdf, "n"; // http://www.planet-php.org/rdf/ } if (isset($links->rss)) { echo $links->rss, "n"; // http://www.planet-php.org/rss/ } if (isset($links->atom)) { echo $links->atom, "n"; // http://www.planet-php.org/atom/ } Based on these links, you can then import from whichever source you wish in the usual manner. This quick method only gives you one link for each feed type, but websites may indicate many links of any type. Perhaps it’s a news site with a RSS feed for each news category. You can iterate over all links using the ArrayObject’s iterator. 1 $links = ZendFeedReader::findFeedLinks(’http://www.planet-php.net’); 2 3 4 5 foreach ($links as $link) { echo $link[’href’], "n"; } 420 Chapter 102. ZendFeedReaderReader
  • 459. Zend Framework 2 Documentation, Release 2.2.5 102.6 Attribute Collections In an attempt to simplify return types, return types from the various feed and entry level methods may include an object of type ZendFeedReaderCollectionAbstractCollection. Despite the special class name which I’ll explain below, this is just a simple subclass of SPL‘s ArrayObject. The main purpose here is to allow the presentation of as much data as possible from the requested elements, while still allowing access to the most relevant data as a simple array. This also enforces a standard approach to returning such data which previously may have wandered between arrays and objects. The new class type acts identically to ArrayObject with the sole addition being a new method getValues() which returns a simple flat array containing the most relevant information. A simple example of this is ZendFeedReaderReaderFeedInterface::getCategories(). When used with any RSS or Atom feed, this method will return category data as a container object called ZendFeedReaderCollectionCategory. The container object will contain, per category, three fields of data: term, scheme and label. The “term” is the basic category name, often machine readable (i.e. plays nice with URIs). The scheme represents a categorisation scheme (usually a URI identifier) also known as a “domain” in RSS 2.0. The “label” is a human readable category name which supports HTML entities. In RSS 2.0, there is no label attribute so it is always set to the same value as the term for convenience. To access category labels by themselves in a simple value array, you might commit to something like: 1 2 3 4 5 6 $feed = ZendFeedReaderReader::import(’http://www.example.com/atom.xml’); $categories = $feed->getCategories(); $labels = array(); foreach ($categories as $cat) { $labels[] = $cat[’label’] } It’s a contrived example, but the point is that the labels are tied up with other information. However, the container class allows you to access the “most relevant” data as a simple array using the getValues() method. The concept of “most relevant” is obviously a judgement call. For categories it means the category labels (not the terms or schemes) while for authors it would be the authors’ names (not their email addresses or URIs). The simple array is flat (just values) and passed through array_unique() to remove duplication. 1 2 3 $feed = ZendFeedReaderReader::import(’http://www.example.com/atom.xml’); $categories = $feed->getCategories(); $labels = $categories->getValues(); The above example shows how to extract only labels and nothing else thus giving simple access to the category labels without any additional work to extract that data by itself. 102.7 Retrieving Feed Information Retrieving information from a feed (we’ll cover entries and items in the next section though they follow identical principals) uses a clearly defined API which is exactly the same regardless of whether the feed in question is RSS, RDF or Atom. The same goes for sub-versions of these standards and we’ve tested every single RSS and Atom version. While the underlying feed XML can differ substantially in terms of the tags and elements they present, they nonetheless are all trying to convey similar information and to reflect this all the differences and wrangling over alternative tags are handled internally by ZendFeedReaderReader presenting you with an identical interface for each. Ideally, you should not have to care whether a feed is RSS or Atom so long as you can extract the information you want. Note: While determining common ground between feed types is itself complex, it should be noted that RSS in particular is a constantly disputed “specification”. This has its roots in the original RSS 2.0 document which contains 102.6. Attribute Collections 421
  • 460. Zend Framework 2 Documentation, Release 2.2.5 ambiguities and does not detail the correct treatment of all elements. As a result, this component rigorously applies the RSS 2.0.11 Specification published by the RSS Advisory Board and its accompanying RSS Best Practices Profile. No other interpretation of RSS 2.0 will be supported though exceptions may be allowed where it does not directly prevent the application of the two documents mentioned above. Of course, we don’t live in an ideal world so there may be times the API just does not cover what you’re looking for. To assist you, ZendFeedReaderReader offers a plugin system which allows you to write Extensions to expand the core API and cover any additional data you are trying to extract from feeds. If writing another Extension is too much trouble, you can simply grab the underlying DOM or XPath objects and do it by hand in your application. Of course, we really do encourage writing an Extension simply to make it more portable and reusable, and useful Extensions may be proposed to the Framework for formal addition. Here’s a summary of the Core API for Feeds. You should note it comprises not only the basic RSS and Atom standards, but also accounts for a number of included Extensions bundled with ZendFeedReaderReader. The naming of these Extension sourced methods remain fairly generic - all Extension methods operate at the same level as the Core API though we do allow you to retrieve any specific Extension object separately if required. 422 Chapter 102. ZendFeedReaderReader
  • 461. Zend Framework 2 Documentation, Release 2.2.5 Table 102.1: Feed Level API Methods getId() getTitle() getDescription() getLink() getFeedLink() getAuthors() getAuthor(integer $index = 0) getDateCreated() getDateModified() getLastBuildDate() getLanguage() getGenerator() getCopyright() getHubs() getCategories() getImage() Returns a unique ID associated with this feed Returns the title of the feed Returns the text description of the feed. Returns a URI to the HTML website containing the same or similar information as this feed (i.e. if the feed is from a blog, it should provide the blog’s URI where the HTML version of the entries can be read). Returns the URI of this feed, which may be the same as the URI used to import the feed. There are important cases where the feed link may differ because the source URI is being updated and is intended to be removed in the future. Returns an object of type ZendFeedReaderCollectionAuthor which is an ArrayObject whose elements are each simple arrays containing any combination of the keys “name”, “email” and “uri”. Where irrelevant to the source data, some of these keys may be omitted. Returns either the first author known, or with the optional $index parameter any specific index on the array of Authors as described above (returning NULL if an invalid index). Returns the date on which this feed was created. Generally only applicable to Atom where it represents the date the resource described by an Atom 1.0 document was created. The returned date will be a DateTime object. Returns the date on which this feed was last modified. The returned date will be a DateTime object. Returns the date on which this feed was last built. The returned date will be a DateTime object. This is only supported by RSS - Atom feeds will always return NULL. Returns the language of the feed (if defined) or simply the language noted in the XML document. Returns the generator of the feed, e.g. the software which generated it. This may differ between RSS and Atom since Atom defines a different notation. Returns any copyright notice associated with the feed. Returns an array of all Hub Server URI endpoints which are advertised by the feed for use with the Pubsubhubbub Protocol, allowing subscriptions to the feed for real-time updates. Returns a ZendFeedReaderCollectionCategory object containing the details of any categories associated with the overall feed. The supported fields include “term” (the machine readable category name), “scheme” (the categorisation scheme and domain for this category), and “label” (a HTML decoded human readable category name). Where any of the three fields are absent from the field, they are either set to the closest available alternative or, in the case of “scheme”, set to NULL. Returns an array containing data relating to any feed image or logo, or NULL if no image found. The resulting array may contain the following keys: uri, link, title, description, height, and width. Atom logos only contain a URI so the remaining metadata is drawn from RSS feeds only. Given the variety of feeds in the wild, some of these methods will undoubtedly return NULL indicating the relevant information couldn’t be located. Where possible, ZendFeedReaderReader will fall back on alternative elements during its search. For example, searching an RSS feed for a modification date is more complicated than it looks. RSS 2.0 feeds should include a <lastBuildDate> tag and (or) a <pubDate> element. But what if it doesn’t, maybe this is an RSS 1.0 feed? Perhaps it instead has an <atom:updated> element with identical information (Atom may be used to supplement RSS‘s syntax)? Failing that, we could simply look at the entries, pick the most recent, and use its <pubDate> element. Assuming it exists... Many feeds also use Dublin Core 1.0 or 1.1 <dc:date> elements for feeds and entries. Or we could find Atom lurking again. The point is, ZendFeedReaderReader was designed to know this. When you ask for the modification date (or anything else), it will run off and search for all these alternatives until it either gives up and returns NULL, or finds an alternative that should have the right answer. 102.7. Retrieving Feed Information 423
  • 462. Zend Framework 2 Documentation, Release 2.2.5 In addition to the above methods, all Feed objects implement methods for retrieving the DOM and XPath objects for the current feeds as described earlier. Feed objects also implement the SPL Iterator and Countable interfaces. The extended API is summarised below. Table 102.2: Extended Feed Level API Methods getDomDocument() getElement() saveXml() getXpath() getXpathPrefix() getEncoding() count() current() key() next() rewind() valid() getExtensions() getExtension(string $name) getType() Returns the parent DOMDocument object for the entire source XML document Returns the current feed level DOMElement object Returns a string containing an XML document of the entire feed element (this is not the original document but a rebuilt version) Returns the DOMXPath object used internally to run queries on the DOMDocument object (this includes core and Extension namespaces pre-registered) Returns the valid DOM path prefix prepended to all XPath queries matching the feed being queried Returns the encoding of the source XML document (note: this cannot account for errors such as the server sending documents in a different encoding). Where not defined, the default UTF-8 encoding of Unicode is applied. Returns a count of the entries or items this feed contains (implements SPLCountable interface) Returns either the current entry (using the current index from key()) Returns the current entry index Increments the entry index value by one Resets the entry index to 0 Checks that the current entry index is valid, i.e. it does fall below 0 and does not exceed the number of entries existing. Returns an array of all Extension objects loaded for the current feed (note: both feed-level and entry-level Extensions exist, and only feed-level Extensions are returned here). The array keys are of the form {ExtensionName}_Feed. Returns an Extension object for the feed registered under the provided name. This allows more fine-grained access to Extensions which may otherwise be hidden within the implementation of the standard API methods. Returns a static class constant (e.g. ZendFeedReaderReader::TYPE_ATOM_03, i.e. Atom 0.3) indicating exactly what kind of feed is being consumed. 102.8 Retrieving Entry/Item Information Retrieving information for specific entries or items (depending on whether you speak Atom or RSS) is identical to feed level data. Accessing entries is simply a matter of iterating over a Feed object or using the SPL Iterator interface Feed objects implement and calling the appropriate method on each. 424 Chapter 102. ZendFeedReaderReader
  • 463. Zend Framework 2 Documentation, Release 2.2.5 Table 102.3: Entry Level API Methods getId() getTitle() getDescription() getLink() getPermaLink() getAuthors() getAuthor(integer $index = 0) getDateCreated() getDateModified() getContent() getEnclosure() getCommentCount() getCommentLink() getCommentFeedLink([string $type = ‘atom’|’rss’]) getCategories() Returns a unique ID for the current entry. Returns the title of the current entry. Returns a description of the current entry. Returns a URI to the HTML version of the current entry. Returns the permanent link to the current entry. In most cases, this is the same as using getLink(). Returns an object of type ZendFeedReaderCollectionAuthor which is an ArrayObject whose elements are each simple arrays containing any combination of the keys “name”, “email” and uri”. Where irrelevant to the source data, some of these keys may be omitted. Returns either the first author known, or with the optional $index parameter any specific index on the array of Authors as described above (returning NULL if an invalid index). Returns the date on which the current entry was created. Generally only applicable to Atom where it represents the date the resource described by an Atom 1.0 document was created. Returns the date on which the current entry was last modified Returns the content of the current entry (this has any entities reversed if possible assuming the content type is HTML). The description is returned if a separate content element does not exist. Returns an array containing the value of all attributes from a multi-media <enclosure> element including as array keys: url, length, type. In accordance with the RSS Best Practices Profile of the RSS Advisory Board, no support is offers for multiple enclosures since such support forms no part of the RSS specification. Returns the number of comments made on this entry at the time the feed was last generated Returns a URI pointing to the HTML page where comments can be made on this entry Returns a URI pointing to a feed of the provided type containing all comments for this entry (type defaults to Atom/RSS depending on current feed type). Returns a ZendFeedReaderCollectionCategory object containing the details of any categories associated with the entry. The supported fields include “term” (the machine readable category name), “scheme” (the categorisation scheme and domain for this category), and “label” (a HTML decoded human readable category name). Where any of the three fields are absent from the field, they are either set to the closest available alternative or, in the case of “scheme”, set to NULL. The extended API for entries is identical to that for feeds with the exception of the Iterator methods which are not needed here. 102.8. Retrieving Entry/Item Information 425
  • 464. Zend Framework 2 Documentation, Release 2.2.5 Caution: There is often confusion over the concepts of modified and created dates. In Atom, these are two clearly defined concepts (so knock yourself out) but in RSS they are vague. RSS 2.0 defines a single <pubDate> element which typically refers to the date this entry was published, i.e. a creation date of sorts. This is not always the case, and it may change with updates or not. As a result, if you really want to check whether an entry has changed, don’t rely on the results of getDateModified(). Instead, consider tracking the MD5 hash of three other elements concatenated, e.g. using getTitle(), getDescription() and getContent(). If the entry was truly updated, this hash computation will give a different result than previously saved hashes for the same entry. This is obviously content oriented, and will not assist in detecting changes to other relevant elements. Atom feeds should not require such steps. Further muddying the waters, dates in feeds may follow different standards. Atom and Dublin Core dates should follow ISO 8601, and RSS dates should follow RFC 822 or RFC 2822 which is also common. Date methods will throw an exception if DateTime cannot load the date string using one of the above standards, or the PHP recognised possibilities for RSS dates. Warning: The values returned from these methods are not validated. This means users must perform validation on all retrieved data including the filtering of any HTML such as from getContent() before it is output from your application. Remember that most feeds come from external sources, and therefore the default assumption should be that they cannot be trusted. Table 102.4: Extended Entry Level API Methods getDomDocument() getElement() getXpath() getXpathPrefix() getEncoding() getExtensions() getExtension(string $name) getType() Returns the parent DOMDocument object for the entire feed (not just the current entry) Returns the current entry level DOMElement object Returns the DOMXPath object used internally to run queries on the DOMDocument object (this includes core and Extension namespaces pre-registered) Returns the valid DOM path prefix prepended to all XPath queries matching the entry being queried Returns the encoding of the source XML document (note: this cannot account for errors such as the server sending documents in a different encoding). The default encoding applied in the absence of any other is the UTF-8 encoding of Unicode. Returns an array of all Extension objects loaded for the current entry (note: both feed-level and entry-level Extensions exist, and only entry-level Extensions are returned here). The array keys are in the form {ExtensionName}Entry. Returns an Extension object for the entry registered under the provided name. This allows more fine-grained access to Extensions which may otherwise be hidden within the implementation of the standard API methods. Returns a static class constant (e.g. ZendFeedReaderReader::TYPE_ATOM_03, i.e. Atom 0.3) indicating exactly what kind of feed is being consumed. 102.9 Extending Feed and Entry APIs Extending ZendFeedReaderReader allows you to add methods at both the feed and entry level which cover the retrieval of information not already supported by ZendFeedReaderReader. Given the number of RSS and Atom extensions that exist, this is a good thing since ZendFeedReaderReader couldn’t possibly add everything. There are two types of Extensions possible, those which retrieve information from elements which are immediate children of the root element (e.g. <channel> for RSS or <feed> for Atom) and those who retrieve information from child elements of an entry (e.g. <item> for RSS or <entry> for Atom). On the filesystem these are grouped as classes within a namespace based on the extension stan- 426 Chapter 102. ZendFeedReaderReader
  • 465. Zend Framework 2 Documentation, Release 2.2.5 dard’s name. For example, internally we have ZendFeedReaderExtensionDublinCoreFeed and ZendFeedReaderExtensionDublinCoreEntry classes which are two Extensions implementing Dublin Core 1.0 and 1.1 support. Extensions are loaded into ZendFeedReaderReader using a ZendServiceManagerAbstractPluginManager implementation, ZendFeedReaderExtensionManager, so its operation will be familiar from other Zend Framework components. ZendFeedReaderReader already bundles a number of these Extensions, however those which are not used internally and registered by default (so called Core Extensions) must be registered to ZendFeedReaderReader before they are used. The bundled Extensions include: Table 102.5: Core Extensions (pre-registered) DublinCore (Feed and Entry) Content (Entry only) Atom (Feed and Entry) Slash WellFormedWeb Thread Podcast Implements support for Dublin Core Metadata Element Set 1.0 and 1.1 Implements support for Content 1.0 Implements support for Atom 0.3 and Atom 1.0 Implements support for the Slash RSS 1.0 module Implements support for the Well Formed Web CommentAPI 1.0 Implements support for Atom Threading Extensions as described in RFC 4685 Implements support for the Podcast 1.0 DTD from Apple The Core Extensions are somewhat special since they are extremely common and multi-faceted. For example, we have a Core Extension for Atom. Atom is implemented as an Extension (not just a base class) because it doubles as a valid RSS module - you can insert Atom elements into RSS feeds. I’ve even seen RDF feeds which use a lot of Atom in place of more common Extensions like Dublin Core. Table 102.6: Non-Core Extensions (must register manually) Syndication CreativeCommons Implements Syndication 1.0 support for RSS feeds A RSS module that adds an element at the <channel> or <item> level that specifies which Creative Commons license applies. The additional non-Core Extensions are offered but not registered to ZendFeedReaderReader by default. If you want to use them, you’ll need to tell ZendFeedReaderReader to load them in advance of importing a feed. Additional non-Core Extensions will be included in future iterations of the component. Registering an Extension with ZendFeedReaderReader, so it is loaded and its API is available to Feed and Entry objects, is a simple affair using the ZendFeedReaderExtensionManager. Here we register the optional Syndication Extension, and discover that it can be directly called from the Entry level API without any effort. Note that Extension names are case sensitive and use camel casing for multiple terms. 1 2 3 ZendFeedReaderReader::registerExtension(’Syndication’); $feed = ZendFeedReaderReader::import(’http://rss.slashdot.org/Slashdot/slashdot’); $updatePeriod = $feed->getUpdatePeriod(); In the simple example above, we checked how frequently a feed is being updated using the getUpdatePeriod() method. Since it’s not part of ZendFeedReaderReader‘s core API, it could only be a method supported by the newly registered Syndication Extension. As you can also notice, the new methods from Extensions are accessible from the main API using PHP‘s magic methods. As an alternative, you can also directly access any Extension object for a similar result as seen below. 1 2 3 4 ZendFeedReaderReader::registerExtension(’Syndication’); $feed = ZendFeedReaderReader::import(’http://rss.slashdot.org/Slashdot/slashdot’); $syndication = $feed->getExtension(’Syndication’); $updatePeriod = $syndication->getUpdatePeriod(); 102.9. Extending Feed and Entry APIs 427
  • 466. Zend Framework 2 Documentation, Release 2.2.5 102.9.1 Writing ZendFeedReaderReader Extensions Inevitably, there will be times when the ZendFeedReaderReader API is just not capable of getting something you need from a feed or entry. You can use the underlying source objects, like DOMDocument, to get these by hand however there is a more reusable method available by writing Extensions supporting these new queries. As an example, let’s take the case of a purely fictitious corporation named Jungle Books. Jungle Books have been publishing a lot of reviews on books they sell (from external sources and customers), which are distributed as an RSS 2.0 feed. Their marketing department realises that web applications using this feed cannot currently figure out exactly what book is being reviewed. To make life easier for everyone, they determine that the geek department needs to extend RSS 2.0 to include a new element per entry supplying the ISBN-10 or ISBN-13 number of the publication the entry concerns. They define the new <isbn> element quite simply with a standard name and namespace URI: 1 2 JungleBooks 1.0: http://example.com/junglebooks/rss/module/1.0/ A snippet of RSS containing this extension in practice could be something similar to: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?xml version="1.0" encoding="utf-8" ?> <rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:jungle="http://example.com/junglebooks/rss/module/1.0/"> <channel> <title>Jungle Books Customer Reviews</title> <link>http://example.com/junglebooks</link> <description>Many book reviews!</description> <pubDate>Fri, 26 Jun 2009 19:15:10 GMT</pubDate> <jungle:dayPopular> http://example.com/junglebooks/book/938 </jungle:dayPopular> <item> <title>Review Of Flatland: A Romance of Many Dimensions</title> <link>http://example.com/junglebooks/review/987</link> <author>Confused Physics Student</author> <content:encoded> A romantic square?! </content:encoded> <pubDate>Thu, 25 Jun 2009 20:03:28 -0700</pubDate> <jungle:isbn>048627263X</jungle:isbn> </item> </channel> </rss> Implementing this new ISBN element as a simple entry level extension would require the following class (using your own class namespace outside of Zend). 1 2 3 4 5 6 7 8 9 10 11 12 13 class MyFeedReaderExtensionJungleBooksEntry extends ZendFeedReaderExtensionAbstractEntry { public function getIsbn() { if (isset($this->data[’isbn’])) { return $this->data[’isbn’]; } $isbn = $this->xpath->evaluate( ’string(’ . $this->getXpathPrefix() . ’/jungle:isbn)’ ); if (!$isbn) { $isbn = null; 428 Chapter 102. ZendFeedReaderReader
  • 467. Zend Framework 2 Documentation, Release 2.2.5 } $this->data[’isbn’] = $isbn; return $this->data[’isbn’]; 14 15 16 } 17 18 protected function registerNamespaces() { $this->xpath->registerNamespace( ’jungle’, ’http://example.com/junglebooks/rss/module/1.0/’ ); } 19 20 21 22 23 24 25 } This extension is easy enough to follow. It creates a new method getIsbn() which runs an XPath query on the current entry to extract the ISBN number enclosed by the <jungle:isbn> element. It can optionally store this to the internal non-persistent cache (no need to keep querying the DOM if it’s called again on the same entry). The value is returned to the caller. At the end we have a protected method (it’s abstract so it must exist) which registers the Jungle Books namespace for their custom RSS module. While we call this an RSS module, there’s nothing to prevent the same element being used in Atom feeds - and all Extensions which use the prefix provided by getXpathPrefix() are actually neutral and work on RSS or Atom feeds with no extra code. Since this Extension is stored outside of Zend Framework, you’ll need to register the path prefix for your Extensions so ZendLoaderPluginLoader can find them. After that, it’s merely a matter of registering the Extension, if it’s not already loaded, and using it in practice. 1 2 3 4 5 6 if (!ZendFeedReaderReader::isRegistered(’JungleBooks’)) { $extensions = ZendFeedReaderReader::getExtensionManager(); $extensions->setInvokableClass(’JungleBooksEntry’, ’MyFeedReaderExtensionJungleBooksEntry’); ZendFeedReaderReader::registerExtension(’JungleBooks’); } $feed = ZendFeedReaderReader::import(’http://example.com/junglebooks/rss’); 7 8 9 // ISBN for whatever book the first entry in the feed was concerned with $firstIsbn = $feed->current()->getIsbn(); Writing a feed level Extension is not much different. The example feed from earlier included an unmentioned <jungle:dayPopular> element which Jungle Books have added to their standard to include a link to the day’s most popular book (in terms of visitor traffic). Here’s an Extension which adds a getDaysPopularBookLink() method to the feel level API. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class MyFeedReaderExtensionJungleBooksFeed extends ZendFeedReaderExtensionAbstractFeed { public function getDaysPopularBookLink() { if (isset($this->data[’dayPopular’])) { return $this->data[’dayPopular’]; } $dayPopular = $this->xpath->evaluate( ’string(’ . $this->getXpathPrefix() . ’/jungle:dayPopular)’ ); if (!$dayPopular) { $dayPopular = null; } $this->data[’dayPopular’] = $dayPopular; return $this->data[’dayPopular’]; } 18 102.9. Extending Feed and Entry APIs 429
  • 468. Zend Framework 2 Documentation, Release 2.2.5 protected function registerNamespaces() { $this->xpath->registerNamespace( ’jungle’, ’http://example.com/junglebooks/rss/module/1.0/’ ); } 19 20 21 22 23 24 25 } Let’s repeat the last example using a custom Extension to show the method being used. 1 2 3 4 5 6 if (!ZendFeedReaderReader::isRegistered(’JungleBooks’)) { $extensions = ZendFeedReaderReader::getExtensionManager(); $extensions->setInvokableClass(’JungleBooksFeed’, ’MyFeedReaderExtensionJungleBooksFeed’); ZendFeedReaderReader::registerExtension(’JungleBooks’); } $feed = ZendFeedReaderReader::import(’http://example.com/junglebooks/rss’); 7 8 9 // URI to the information page of the day’s most popular book with visitors $daysPopularBookLink = $feed->getDaysPopularBookLink(); Going through these examples, you’ll note that we don’t register feed and entry Extensions separately. Extensions within the same standard may or may not include both a feed and entry class, so ZendFeedReaderReader only requires you to register the overall parent name, e.g. JungleBooks, DublinCore, Slash. Internally, it can check at what level Extensions exist and load them up if found. In our case, we have a full set of Extensions now: JungleBooksFeed and JungleBooksEntry. 430 Chapter 102. ZendFeedReaderReader
  • 469. CHAPTER 103 ZendFeedWriterWriter 103.1 Introduction ZendFeedWriterWriter is the sibling component to ZendFeedReaderReader responsible for generating feeds for output. It supports the Atom 1.0 specification (RFC 4287) and RSS 2.0 as specified by the RSS Advisory Board (RSS 2.0.11). It does not deviate from these standards. It does, however, offer a simple Extension system which allows for any extension and module for either of these two specifications to be implemented if they are not provided out of the box. In many ways, ZendFeedWriterWriter is the inverse of ZendFeedReaderReader. Where ZendFeedReaderReader focuses on providing an easy to use architecture fronted by getter methods, ZendFeedWriterWriter is fronted by similarly named setters or mutators. This ensures the API won’t pose a learning curve to anyone familiar with ZendFeedReaderReader. As a result of this design, the rest may even be obvious. Behind the scenes, data set on any ZendFeedWriterWriter Data Container object is translated at render time onto a DOMDocument object using the necessary feed elements. For each supported feed type there is both an Atom 1.0 and RSS 2.0 renderer. Using a DOMDocument class rather than a templating solution has numerous advantages, the most obvious being the ability to export the DOMDocument for additional processing and relying on PHP DOM for correct and valid rendering. 103.2 Architecture The architecture of ZendFeedWriterWriter is very simple. It has two core sets of classes: data containers and renderers. The containers include the ZendFeedWriterFeed and ZendFeedWriterEntry classes. The Entry classes can be attached to any Feed class. The sole purpose of these containers is to collect data about the feed to generate using a simple interface of setter methods. These methods perform some data validity testing. For example, it will validate any passed URIs, dates, etc. These checks are not tied to any of the feed standards definitions. The container objects also contain methods to allow for fast rendering and export of the final feed, and these can be reused at will. In addition to the main data container classes, there are two additional Atom 2.0 specific classes. ZendFeedWriterSource and ZendFeedWriterDeleted. The former implements Atom 2.0 source elements which carry source feed metadata for a specific entry within an aggregate feed (i.e. the current feed is not 431
  • 470. Zend Framework 2 Documentation, Release 2.2.5 the entry’s original source). The latter implements the Atom Tombstones RFC allowing feeds to carry references to entries which have been deleted. While there are two main data container types, there are four renderers - two matching container renderers per supported feed type. Each renderer accepts a container, and based on its content attempts to generate valid feed markup. If the renderer is unable to generate valid feed markup, perhaps due to the container missing an obligatory data point, it will report this by throwing an Exception. While it is possible to ignore Exceptions, this removes the default safeguard of ensuring you have sufficient data set to render a wholly valid feed. To explain this more clearly, you may construct a set of data containers for a feed where there is a Feed container, into which has been added some Entry containers and a Deleted container. This forms a data hierarchy resembling a normal feed. When rendering is performed, this hierarchy has its pieces passed to relevant renderers and the partial feeds (all DOMDocuments) are then pieced together to create a complete feed. In the case of Source or Deleted (Tomestone) containers, these are rendered only for Atom 2.0 and ignored for RSS. Due to the system being divided between data containers and renderers, it can make Extensions somewhat interesting. A typical Extension offering namespaced feed and entry level elements, must itself reflect the exact same architecture, i.e. offer feed and entry level data containers, and matching renderers. There is, fortunately, no complex integration work required since all Extension classes are simply registered and automatically used by the core classes. We’ll meet Extensions in more detail at the end of this section. 103.3 Getting Started Using ZendFeedWriterWriter is as simple as setting data and triggering the renderer. Here is an example to generate a minimal Atom 1.0 feed. As this demonstrates, each feed or entry uses a separate data container. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 /** * Create the parent feed */ $feed = new ZendFeedWriterFeed; $feed->setTitle(’Paddy’s Blog’); $feed->setLink(’http://www.example.com’); $feed->setFeedLink(’http://www.example.com/atom’, ’atom’); $feed->addAuthor(array( ’name’ => ’Paddy’, ’email’ => ’[email protected]’, ’uri’ => ’http://www.example.com’, )); $feed->setDateModified(time()); $feed->addHub(’http://pubsubhubbub.appspot.com/’); 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 /** * Add one or more entries. Note that entries must * be manually added once created. */ $entry = $feed->createEntry(); $entry->setTitle(’All Your Base Are Belong To Us’); $entry->setLink(’http://www.example.com/all-your-base-are-belong-to-us’); $entry->addAuthor(array( ’name’ => ’Paddy’, ’email’ => ’[email protected]’, ’uri’ => ’http://www.example.com’, )); $entry->setDateModified(time()); $entry->setDateCreated(time()); $entry->setDescription(’Exposing the difficultly of porting games to English.’); 432 Chapter 103. ZendFeedWriterWriter
  • 471. Zend Framework 2 Documentation, Release 2.2.5 31 32 33 34 $entry->setContent( ’I am not writing the article. The example is long enough as is ;).’ ); $feed->addEntry($entry); 35 36 37 38 39 40 /** * Render the resulting feed to Atom 1.0 and assign to $out. * You can substitute "atom" with "rss" to generate an RSS 2.0 feed. */ $out = $feed->export(’atom’); The output rendered should be as follows: 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 <?xml version="1.0" encoding="utf-8"?> <feed xmlns="http://www.w3.org/2005/Atom"> <title type="text">Paddy’s Blog</title> <subtitle type="text">Writing about PC Games since 176 BC.</subtitle> <updated>2009-12-14T20:28:18+00:00</updated> <generator uri="http://framework.zend.com" version="1.10.0alpha"> ZendFeedWriter </generator> <link rel="alternate" type="text/html" href="http://www.example.com"/> <link rel="self" type="application/atom+xml" href="http://www.example.com/atom"/> <id>http://www.example.com</id> <author> <name>Paddy</name> <email>[email protected]</email> <uri>http://www.example.com</uri> </author> <link rel="hub" href="http://pubsubhubbub.appspot.com/"/> <entry> <title type="html"><![CDATA[All Your Base Are Belong To Us]]></title> <summary type="html"> <![CDATA[Exposing the difficultly of porting games to English.]]> </summary> <published>2009-12-14T20:28:18+00:00</published> <updated>2009-12-14T20:28:18+00:00</updated> <link rel="alternate" type="text/html" href="http://www.example.com/all-your-base-are-belong-to-us"/> <id>http://www.example.com/all-your-base-are-belong-to-us</id> <author> <name>Paddy</name> <email>[email protected]</email> <uri>http://www.example.com</uri> </author> <content type="html"> <![CDATA[I am not writing the article. The example is long enough as is ;).]]> </content> </entry> </feed> This is a perfectly valid Atom 1.0 example. It should be noted that omitting an obligatory point of data, such as a title, will trigger an Exception when rendering as Atom 1.0. This will differ for RSS 2.0 since a title may be omitted so long as a description is present. This gives rise to Exceptions that differ between the two standards depending on the 103.3. Getting Started 433
  • 472. Zend Framework 2 Documentation, Release 2.2.5 renderer in use. By design, ZendFeedWriterWriter will not render an invalid feed for either standard unless the end-user deliberately elects to ignore all Exceptions. This built in safeguard was added to ensure users without in-depth knowledge of the relevant specifications have a bit less to worry about. 103.4 Setting Feed Data Points Before you can render a feed, you must first setup the data necessary for the feed being rendered. This utilises a simple setter style API which doubles as an initial method for validating the data being set. By design, the API closely matches that for ZendFeedReaderReader to avoid undue confusion and uncertainty. Note: Users have commented that the lack of a simple array based notation for input data gives rise to lengthy tracts of code. This will be addressed in a future release. ZendFeedWriterWriter offers this API via its data container classes ZendFeedWriterFeed and ZendFeedWriterEntry (not to mention the Atom 2.0 specific and Extension classes). These classes merely store all feed data in a type-agnostic manner, meaning you may reuse any data container with any renderer without requiring additional work. Both classes are also amenable to Extensions, meaning that an Extension may define its own container classes which are registered to the base container classes as extensions, and are checked when any method call triggers the base container’s __call() method. Here’s a summary of the Core API for Feeds. You should note it comprises not only the basic RSS and Atom standards, but also accounts for a number of included Extensions bundled with ZendFeedWriterWriter. The naming of these Extension sourced methods remain fairly generic - all Extension methods operate at the same level as the Core API though we do allow you to retrieve any specific Extension object separately if required. The Feed Level API for data is contained in ZendFeedWriterFeed. In addition to the API detailed below, the class also implements the Countable and Iterator interfaces. 434 Chapter 103. ZendFeedWriterWriter
  • 473. Zend Framework 2 Documentation, Release 2.2.5 Table 103.1: Feed Level API Methods setId() setTitle() setDescription() setLink() Set a unique ID associated with this feed. For Atom 1.0 this is an atom:id element, whereas for RSS 2.0 it is added as a guid element. These are optional so long as a link is added, i.e. the link is set as the ID. Set the title of the feed. Set the text description of the feed. Set a URI to the HTML website containing the same or similar information as this feed (i.e. if the feed is from a blog, it should provide the blog’s URI where the HTML version of the entries can be read). setAdd a link to an XML feed, whether the feed being generated or an alternate URI pointing to the FeedLinks() same feed but in a different format. At a minimum, it is recommended to include a link to the feed being generated so it has an identifiable final URI allowing a client to track its location changes without necessitating constant redirects. The parameter is an array of arrays, where each sub-array contains the keys “type” and “uri”. The type should be one of “atom”, “rss”, or “rdf”. addAuSets the data for authors. The parameter is an array of arrays where each sub-array may contain the thors() keys “name”, “email” and “uri”. The “uri” value is only applicable for Atom feeds since RSS contains no facility to show it. For RSS 2.0, rendering will create two elements - an author element containing the email reference with the name in brackets, and a Dublin Core creator element only containing the name. addAuSets the data for a single author following the same array format as described above for a single thor() sub-array. setDateSets the date on which this feed was created. Generally only applicable to Atom where it represents Created() the date the resource described by an Atom 1.0 document was created. The expected parameter may be a UNIX timestamp or a DateTime object. setDateSets the date on which this feed was last modified. The expected parameter may be a UNIX Modified() timestamp or a DateTime object. setLastSets the date on which this feed was last build. The expected parameter may be a UNIX timestamp Buildor a DateTime object. This will only be rendered for RSS 2.0 feeds and is automatically rendered as Date() the current date by default when not explicitly set. setSets the language of the feed. This will be omitted unless set. Language() setGenera- Allows the setting of a generator. The parameter should be an array containing the keys “name”, tor() “version” and “uri”. If omitted a default generator will be added referencing ZendFeedWriter, the current Zend Framework version and the Framework’s URI. setCopySets a copyright notice associated with the feed. right() addHubs() Accepts an array of Pubsubhubbub Hub Endpoints to be rendered in the feed as Atom links so that PuSH Subscribers may subscribe to your feed. Note that you must implement a Pubsubhubbub Publisher in order for real-time updates to be enabled. A Publisher may be implemented using ZendFeedPubsubhubbubPublisher. The method addHub() allows adding a single hub at a time. addCateAccepts an array of categories for rendering, where each element is itself an array whose possible gories() keys include “term”, “label” and “scheme”. The “term” is a typically a category name suitable for inclusion in a URI. The “label” may be a human readable category name supporting special characters (it is HTML encoded during rendering) and is a required key. The “scheme” (called the domain in RSS) is optional but must be a valid URI. The method addCategory() allows adding a single category at a time. setImage() Accepts an array of image metadata for an RSS image or Atom logo. Atom 1.0 only requires a URI. RSS 2.0 requires a URI, HTML link, and an image title. RSS 2.0 optionally may send a width, height and image description. The array parameter may contain these using the keys: uri, link, title, description, height and width. The RSS 2.0 HTML link should point to the feed source’s HTML page. createEnReturns a new instance of ZendFeedWriterEntry. This is the Entry level data container. New entries try() are not automatically assigned to the current feed, so you must explicitly call addEntry() to add the entry for rendering. 103.4. Setting Feed Data Points 435 addEntry() Adds an instance of ZendFeedWriterEntry to the current feed container for rendering. createReturns a new instance of ZendFeedWriterDeleted. This is the Atom 2.0 Tombstone level data Tombcontainer. New entries are not automatically assigned to the current feed, so you must explicitly call
  • 474. Zend Framework 2 Documentation, Release 2.2.5 Note: In addition to these setters, there are also matching getters to retrieve data from the Entry data container. For example, setImage() is matched with a getImage() method. 103.5 Setting Entry Data Points Here’s a summary of the Core API for Entries and Items. You should note it comprises not only the basic RSS and Atom standards, but also accounts for a number of included Extensions bundled with ZendFeedWriterWriter. The naming of these Extension sourced methods remain fairly generic - all Extension methods operate at the same level as the Core API though we do allow you to retrieve any specific Extension object separately if required. The Entry Level API for data is contained in ZendFeedWriterEntry. 436 Chapter 103. ZendFeedWriterWriter
  • 475. Zend Framework 2 Documentation, Release 2.2.5 Table 103.2: Entry Level API Methods setId() setTitle() setDescription() setContent() setLink() setFeedLinks() addAuthors() addAuthor() setDateCreated() setDateModified() setCopyright() setCategories() setCommentCount() setCommentLink() setCommentFeedLink() setCommentFeedLinks() setEncoding() Set a unique ID associated with this entry. For Atom 1.0 this is an atom:id element, whereas for RSS 2.0 it is added as a guid element. These are optional so long as a link is added, i.e. the link is set as the ID. Set the title of the entry. Set the text description of the entry. Set the content of the entry. Set a URI to the HTML website containing the same or similar information as this entry (i.e. if the feed is from a blog, it should provide the blog article’s URI where the HTML version of the entry can be read). Add a link to an XML feed, whether the feed being generated or an alternate URI pointing to the same feed but in a different format. At a minimum, it is recommended to include a link to the feed being generated so it has an identifiable final URI allowing a client to track its location changes without necessitating constant redirects. The parameter is an array of arrays, where each sub-array contains the keys “type” and “uri”. The type should be one of “atom”, “rss”, or “rdf”. If a type is omitted, it defaults to the type used when rendering the feed. Sets the data for authors. The parameter is an array of arrays where each sub-array may contain the keys “name”, “email” and “uri”. The “uri” value is only applicable for Atom feeds since RSS contains no facility to show it. For RSS 2.0, rendering will create two elements - an author element containing the email reference with the name in brackets, and a Dublin Core creator element only containing the name. Sets the data for a single author following the same format as described above for a single sub-array. Sets the date on which this feed was created. Generally only applicable to Atom where it represents the date the resource described by an Atom 1.0 document was created. The expected parameter may be a UNIX timestamp or a DateTime object. If omitted, the date used will be the current date and time. Sets the date on which this feed was last modified. The expected parameter may be a UNIX timestamp or a DateTime object. If omitted, the date used will be the current date and time. Sets a copyright notice associated with the feed. Accepts an array of categories for rendering, where each element is itself an array whose possible keys include “term”, “label” and “scheme”. The “term” is a typically a category name suitable for inclusion in a URI. The “label” may be a human readable category name supporting special characters (it is encoded during rendering) and is a required key. The “scheme” (called the domain in RSS) is optional but must be a valid URI. Sets the number of comments associated with this entry. Rendering differs between RSS and Atom 2.0 depending on the element or attribute needed. Seta a link to a HTML page containing comments associated with this entry. Sets a link to a XML feed containing comments associated with this entry. The parameter is an array containing the keys “uri” and “type”, where the type is one of “rdf”, “rss” or “atom”. Same as setCommentFeedLink() except it accepts an array of arrays, where each subarray contains the expected parameters of setCommentFeedLink(). Sets the encoding of entry text. This will default to UTF-8 which is the preferred encoding. Note: In addition to these setters, there are also matching getters to retrieve data from the Entry data container. 103.5. Setting Entry Data Points 437
  • 476. Zend Framework 2 Documentation, Release 2.2.5 438 Chapter 103. ZendFeedWriterWriter
  • 477. CHAPTER 104 ZendFeedPubSubHubbub ZendFeedPubSubHubbub is an implementation of the PubSubHubbub Core 0.2 Specification (Working Draft). It offers implementations of a Pubsubhubbub Publisher and Subscriber suited to Zend Framework and other PHP applications. 104.1 What is PubSubHubbub? Pubsubhubbub is an open, simple web-scale pubsub protocol. A common use case to enable blogs (Publishers) to “push” updates from their RSS or Atom feeds (Topics) to end Subscribers. These Subscribers will have subscribed to the blog’s RSS or Atom feed via a Hub, a central server which is notified of any updates by the Publisher and which then distributes these updates to all Subscribers. Any feed may advertise that it supports one or more Hubs using an Atom namespaced link element with a rel attribute of “hub”. Pubsubhubbub has garnered attention because it is a pubsub protocol which is easy to implement and which operates over HTTP. Its philosophy is to replace the traditional model where blog feeds have been polled at regular intervals to detect and retrieve updates. Depending on the frequency of polling, this can take a lot of time to propagate updates to interested parties from planet aggregators to desktop readers. With a pubsub system in place, updates are not simply polled by Subscribers, they are pushed to Subscribers, eliminating any delay. For this reason, Pubsubhubbub forms part of what has been dubbed the real-time web. The protocol does not exist in isolation. Pubsub systems have been around for a while, such as the familiar Jabber Publish-Subscribe protocol, XEP-0060, or the less well known rssCloud (described in 2001). However these have not achieved widespread adoption typically due to either their complexity, poor timing or lack of suitability for web applications. rssCloud, which was recently revived as a response to the appearance of Pubsubhubbub, has also seen its usage increase significantly though it lacks a formal specification and currently does not support Atom 1.0 feeds. Perhaps surprisingly given its relative early age, Pubsubhubbub is already in use including in Google Reader, Feedburner, and there are plugins available for Wordpress blogs. 104.2 Architecture ZendFeedPubSubHubbub implements two sides of the Pubsubhubbub 0.2 Specification: a Publisher and a Subscriber. It does not currently implement a Hub Server though this is in progress for a future Zend Framework release. 439
  • 478. Zend Framework 2 Documentation, Release 2.2.5 A Publisher is responsible for notifying all supported Hubs (many can be supported to add redundancy to the system) of any updates to its feeds, whether they be Atom or RSS based. This is achieved by pinging the supported Hub Servers with the URL of the updated feed. In Pubsubhubbub terminology, any updatable resource capable of being subscribed to is referred to as a Topic. Once a ping is received, the Hub will request the updated feed, process it for updated items, and forward all updates to all Subscribers subscribed to that feed. A Subscriber is any party or application which subscribes to one or more Hubs to receive updates from a Topic hosted by a Publisher. The Subscriber never directly communicates with the Publisher since the Hub acts as an intermediary, accepting subscriptions and sending updates to subscribed Subscribers. The Subscriber therefore communicates only with the Hub, either to subscribe or unsubscribe to Topics, or when it receives updates from the Hub. This communication design (“Fat Pings”) effectively removes the possibility of a “Thundering Herd” issue. This occurs in a pubsub system where the Hub merely informs Subscribers that an update is available, prompting all Subscribers to immediately retrieve the feed from the Publisher giving rise to a traffic spike. In Pubsubhubbub, the Hub distributes the actual update in a “Fat Ping” so the Publisher is not subjected to any traffic spike. ZendFeedPubSubHubbub implements Pubsubhubbub Publishers and Subscribers with the classes ZendFeedPubSubHubbubPublisher and ZendFeedPubSubHubbubSubscriber. In addition, the Subscriber implementation may handle any feed updates forwarded from a Hub by using ZendFeedPubSubHubbubSubscriberCallback. These classes, their use cases, and APIs are covered in subsequent sections. 104.3 ZendFeedPubSubHubbubPublisher In Pubsubhubbub, the Publisher is the party who publishes a live feed and frequently updates it with new content. This may be a blog, an aggregator, or even a web service with a public feed based API. In order for these updates to be pushed to Subscribers, the Publisher must notify all of its supported Hubs that an update has occurred using a simple HTTP POST request containing the URI or the updated Topic (i.e the updated RSS or Atom feed). The Hub will confirm receipt of the notification, fetch the updated feed, and forward any updates to any Subscribers who have subscribed to that Hub for updates from the relevant feed. By design, this means the Publisher has very little to do except send these Hub pings whenever its feeds change. As a result, the Publisher implementation is extremely simple to use and requires very little work to setup and use when feeds are updated. ZendFeedPubSubHubbubPublisher implements a full Pubsubhubbub Publisher. Its setup for use is also simple, requiring mainly that it is configured with the URI endpoint for all Hubs to be notified of updates, and the URIs of all Topics to be included in the notifications. The following example shows a Publisher notifying a collection of Hubs about updates to a pair of local RSS and Atom feeds. The class retains a collection of errors which include the Hub URLs, so the notification can be re-attempted later and/or logged if any notifications happen to fail. Each resulting error array also includes a “response” key containing the related HTTP response object. In the event of any errors, it is strongly recommended to attempt the operation for failed Hub Endpoints at least once more at a future time. This may require the use of either a scheduled task for this purpose or a job queue though such extra steps are optional. 1 2 3 4 5 6 7 8 9 10 $publisher = new ZendFeedPubSubHubbubPublisher; $publisher->addHubUrls(array( ’http://pubsubhubbub.appspot.com/’, ’http://hubbub.example.com’, )); $publisher->addUpdatedTopicUrls(array( ’http://www.example.net/rss’, ’http://www.example.net/atom’, )); $publisher->notifyAll(); 11 440 Chapter 104. ZendFeedPubSubHubbub
  • 479. Zend Framework 2 Documentation, Release 2.2.5 12 13 14 15 16 17 18 19 if (!$publisher->isSuccess()) { // check for errors $errors = $publisher->getErrors(); $failedHubs = array(); foreach ($errors as $error) { $failedHubs[] = $error[’hubUrl’]; } } 20 21 // reschedule notifications for the failed Hubs in $failedHubs If you prefer having more concrete control over the Publisher, the methods addHubUrls() and addUpdatedTopicUrls() pass each array value to the singular addHubUrl() and addUpdatedTopicUrl() public methods. There are also matching removeUpdatedTopicUrl() and removeHubUrl() methods. You can also skip setting Hub URIs, and notify each in turn using the notifyHub() method which accepts the URI of a Hub endpoint as its only argument. There are no other tasks to cover. The Publisher implementation is very simple since most of the feed processing and distribution is handled by the selected Hubs. It is however important to detect errors and reschedule notifications as soon as possible (with a reasonable maximum number of retries) to ensure notifications reach all Subscribers. In many cases as a final alternative, Hubs may frequently poll your feeds to offer some additional tolerance for failures both in terms of their own temporary downtime or Publisher errors or downtime. 104.4 ZendFeedPubSubHubbubSubscriber In Pubsubhubbub, the Subscriber is the party who wishes to receive updates to any Topic (RSS or Atom feed). They achieve this by subscribing to one or more of the Hubs advertised by that Topic, usually as a set of one or more Atom 1.0 links with a rel attribute of “hub”. The Hub from that point forward will send an Atom or RSS feed containing all updates to that Subscriber’s Callback URL when it receives an update notification from the Publisher. In this way, the Subscriber need never actually visit the original feed (though it’s still recommended at some level to ensure updates are retrieved if ever a Hub goes offline). All subscription requests must contain the URI of the Topic being subscribed and a Callback URL which the Hub will use to confirm the subscription and to forward updates. The Subscriber therefore has two roles. To create and manage subscriptions, including subscribing for new Topics with a Hub, unsubscribing (if necessary), and periodically renewing subscriptions since they may have a limited validity as set by the Hub. This is handled by ZendFeedPubSubHubbubSubscriber. The second role is to accept updates sent by a Hub to the Subscriber’s Callback URL, i.e. the URI the Subscriber has assigned to handle updates. The Callback URL also handles events where the Hub contacts the Subscriber to confirm all subscriptions and unsubscriptions. This is handled by using an instance of ZendFeedPubSubHubbubSubscriberCallback when the Callback URL is accessed. Important: ZendFeedPubSubHubbubSubscriber implements the Pubsubhubbub 0.2 Specification. As this is a new specification version not all Hubs currently implement it. The new specification allows the Callback URL to include a query string which is used by this class, but not supported by all Hubs. In the interests of maximising compatibility it is therefore recommended that the query string component of the Subscriber Callback URI be presented as a path element, i.e. recognised as a parameter in the route associated with the Callback URI and used by the application’s Router. 104.4. ZendFeedPubSubHubbubSubscriber 441
  • 480. Zend Framework 2 Documentation, Release 2.2.5 104.4.1 Subscribing and Unsubscribing ZendFeedPubSubHubbubSubscriber implements a full Pubsubhubbub Subscriber capable of subscribing to, or unsubscribing from, any Topic via any Hub advertised by that Topic. It operates in conjunction with ZendFeedPubSubHubbubSubscriberCallback which accepts requests from a Hub to confirm all subscription or unsubscription attempts (to prevent third-party misuse). Any subscription (or unsubscription) requires the relevant information before proceeding, i.e. the URI of the Topic (Atom or RSS feed) to be subscribed to for updates, and the URI of the endpoint for the Hub which will handle the subscription and forwarding of the updates. The lifetime of a subscription may be determined by the Hub but most Hubs should support automatic subscription refreshes by checking with the Subscriber. This is supported by ZendFeedPubSubHubbubSubscriberCallback and requires no other work on your part. It is still strongly recommended that you use the Hub sourced subscription time to live (ttl) to schedule the creation of new subscriptions (the process is identical to that for any new subscription) to refresh it with the Hub. While it should not be necessary per se, it covers cases where a Hub may not support automatic subscription refreshing and rules out Hub errors for additional redundancy. With the relevant information to hand, a subscription can be attempted as demonstrated below: 1 $storage = new ZendFeedPubSubHubbubModelSubscription; 2 3 4 5 6 7 8 $subscriber = new ZendFeedPubSubHubbubSubscriber; $subscriber->setStorage($storage); $subscriber->addHubUrl(’http://hubbub.example.com’); $subscriber->setTopicUrl(’http://www.example.net/rss.xml’); $subscriber->setCallbackUrl(’http://www.mydomain.com/hubbub/callback’); $subscriber->subscribeAll(); In order to store subscriptions and offer access to this data for general use, the component requires a database (a schema is provided later in this section). By default, it is assumed the table name is “subscription” and it utilises ZendDbTableAbstract in the background meaning it will use the default adapter you have set for your application. You may also pass a specific custom ZendDbTableAbstract instance into the associated model ZendFeedPubSubHubbubModelSubscription. This custom adapter may be as simple in intent as changing the table name to use or as complex as you deem necessary. While this Model is offered as a default ready-to-roll solution, you may create your own Model using any other backend or database layer (e.g. Doctrine) so long as the resulting class implements the interface ZendFeedPubSubHubbubModelSubscriptionInterface. An example schema (MySQL) for a subscription table accessible by the provided model may look similar to: 1 2 3 4 5 6 7 8 9 10 11 12 CREATE TABLE IF NOT EXISTS ‘subscription‘ ( ‘id‘ varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT ’’, ‘topic_url‘ varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, ‘hub_url‘ varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, ‘created_time‘ datetime DEFAULT NULL, ‘lease_seconds‘ bigint(20) DEFAULT NULL, ‘verify_token‘ varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, ‘secret‘ varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, ‘expiration_time‘ datetime DEFAULT NULL, ‘subscription_state‘ varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (‘id‘) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; Behind the scenes, the Subscriber above will send a request to the Hub endpoint containing the following parameters (based on the previous example): 442 Chapter 104. ZendFeedPubSubHubbub
  • 481. Zend Framework 2 Documentation, Release 2.2.5 Table 104.1: Subscription request parameters PaValue Explanation rameter hub.callback http://www.mydomain.com/hubbub/callback?xhub.subscription=5536df06b5dcb966edab3a4c4d The URI used by a Hub to contact the Subscriber and either request confirmation of a (un)subscription request or send updates from subscribed feeds. The appended query string contains a custom parameter (hence the xhub designation). It is a query string parameter preserved by the Hub and resent with all Subscriber requests. Its purpose is to allow the Subscriber to identify and look up the subscription associated with any Hub request in a backend storage medium. This is a non=standard parameter used by this component in preference to encoding a subscription key in the URI path which is more difficult to implement in a Zend Framework application. Nevertheless, since not all Hubs support query string parameters, we still strongly recommend adding the subscription key as a path component in the form http://www.mydomain.com/hubbub/callback/5536df06b5 To accomplish this, it requires defining a route capable of parsing out the final value of the key and then retrieving the value and passing it to the Subscriber Callback object. The value would be passed into the method ZendPubSubHubbubSubscriberCallback::setSubscriptionKey(). A detailed example is offered later. hub.lease_seconds 2592000 The number of seconds for which the Subscriber would like a new subscription to remain valid for (i.e. a TTL). Hubs may enforce their own maximum subscription period. All subscriptions should be renewed by simply re-subscribing before the subscription period ends to ensure continuity of updates. Hubs should additionally attempt to automatically refresh subscriptions before they expire by contacting Subscribers (handled automatically by the Callback class). hub.mode subscribe Simple value indicating this is a subscription request. Unsubscription requests would use the “unsubscribe” value. hub.topichttp://www.example.net/rss.xml The URI of the topic (i.e. Atom or RSS feed) which the Subscriber wishes to subscribe to for updates. hub.verifyync s Indicates to the Hub the preferred mode of verifying subscriptions or unsubscriptions. It is repeated twice in order of preference. Technically this component does not distinguish between the two modes and treats both equally. hub.verifysync a Indicates to the Hub the preferred mode of verifying subscriptions or unsubscriptions. It is repeated twice in order of preference. Technically this component does not distinguish between the two modes and treats both equally. hub.verify_token 3065919804abA verification token returned to the Subscriber by the 104.4. ZendFeedPubSubHubbubSubscriber 443 caa7212ae89.879827871253878386 Hub when it is confirming a subscription or unsubscription. Offers a measure of reliance that the confirmation request originates from the correct Hub to prevent misuse.
  • 482. Zend Framework 2 Documentation, Release 2.2.5 You can modify several of these parameters to indicate a different preference. For example, you can set a different lease seconds value using ZendFeedPubSubHubbubSubscriber::setLeaseSeconds() or show a preference for the async verify mode by using setPreferredVerificationMode(ZendFeedPubSubHubbubPubSubHubbub::VERIFICATION_ However the Hubs retain the capability to enforce their own preferences and for this reason the component is deliberately designed to work across almost any set of options with minimum end-user configuration required. Conventions are great when they work! Note: While Hubs may require the use of a specific verification mode (both are supported by ZendFeedPubSubHubbub), you may indicate a specific preference using the setPreferredVerificationMode() method. In “sync” (synchronous) mode, the Hub attempts to confirm a subscription as soon as it is received, and before responding to the subscription request. In “async” (asynchronous) mode, the Hub will return a response to the subscription request immediately, and its verification request may occur at a later time. Since ZendFeedPubSubHubbub implements the Subscriber verification role as a separate callback class and requires the use of a backend storage medium, it actually supports both transparently though in terms of end-user performance, asynchronous verification is very much preferred to eliminate the impact of a poorly performing Hub tying up end-user server resources and connections for too long. Unsubscribing from a Topic follows the exact same pattern as the previous example, with the exception that we should call unsubscribeAll() instead. The parameters included are identical to a subscription request with the exception that “hub.mode” is set to “unsubscribe”. By default, a new instance of ZendPubSubHubbubSubscriber will attempt to use a database backed storage medium which defaults to using the default ZendDb adapter with a table name of “subscription”. It is recommended to set a custom storage solution where these defaults are not apt either by passing in a new Model supporting the required interface or by passing a new instance of ZendDbTableAbstract to the default Model’s constructor to change the used table name. 104.4.2 Handling Subscriber Callbacks Whenever a subscription or unsubscription request is made, the Hub must verify the request by forwarding a new verification request to the Callback URL set in the subscription or unsubscription parameters. To handle these Hub requests, which will include all future communications containing Topic (feed) updates, the Callback URL should trigger the execution of an instance of ZendFeedPubSubHubbubSubscriberCallback to handle the request. The Callback class should be configured to use the same storage medium as the Subscriber class. Using it is quite simple since most of its work is performed internally. 1 2 3 4 5 $storage = new ZendFeedPubSubHubbubModelSubscription; $callback = new ZendFeedPubSubHubbubSubscriberCallback; $callback->setStorage($storage); $callback->handle(); $callback->sendResponse(); 6 7 8 9 10 11 12 13 14 15 16 /** * Check if the callback resulting in the receipt of a feed update. * Otherwise it was either a (un)sub verification request or invalid request. * Typically we need do nothing other than add feed update handling - the rest * is handled internally by the class. */ if ($callback->hasFeedUpdate()) { $feedString = $callback->getFeedUpdate(); /** * Process the feed update asynchronously to avoid a Hub timeout. 444 Chapter 104. ZendFeedPubSubHubbub
  • 483. Zend Framework 2 Documentation, Release 2.2.5 */ 17 18 } Note: It should be noted that ZendFeedPubSubHubbubSubscriberCallback may independently parse any incoming query string and other parameters. This is necessary since PHP alters the structure and keys of a query string when it is parsed into the $_GET or $_POST superglobals. For example, all duplicate keys are ignored and periods are converted to underscores. Pubsubhubbub features both of these in the query strings it generates. Important: It is essential that developers recognise that Hubs are only concerned with sending requests and receiving a response which verifies its receipt. If a feed update is received, it should never be processed on the spot since this leaves the Hub waiting for a response. Rather, any processing should be offloaded to another process or deferred until after a response has been returned to the Hub. One symptom of a failure to promptly complete Hub requests is that a Hub may continue to attempt delivery of the update or verification request leading to duplicated update attempts being processed by the Subscriber. This appears problematic - but in reality a Hub may apply a timeout of just a few seconds, and if no response is received within that time it may disconnect (assuming a delivery failure) and retry later. Note that Hubs are expected to distribute vast volumes of updates so their resources are stretched - please do process feeds asynchronously (e.g. in a separate process or a job queue or even a cron scheduled task) as much as possible. 104.4.3 Setting Up And Using A Callback URL Route As noted earlier, the ZendFeedPubSubHubbubSubscriberCallback class receives the combined key associated with any subscription from the Hub via one of two methods. The technically preferred method is to add this key to the Callback URL employed by the Hub in all future requests using a query string parameter with the key “xhub.subscription”. However, for historical reasons, primarily that this was not supported in Pubsubhubbub 0.1 (it was recently added in 0.2 only), it is strongly recommended to use the most compatible means of adding this key to the Callback URL by appending it to the URL‘s path. Thus the URL http://www.example.com/callback?xhub.subscription=key would become http://www.example.com/callback/key. Since the query string method is the default in anticipation of a greater level of future support for the full 0.2 specification, this requires some additional work to implement. The first step to make the ZendFeedPubSubHubbubSubscriberCallback class aware of the path contained subscription key. It’s manually injected therefore since it also requires manually defining a route for this purpose. This is achieved simply by called the method ZendFeedPubSubHubbubSubscriberCallback::setSubscriptionKey() with the parameter being the key value available from the Router. The example below demonstrates this using a Zend Framework controller. 1 use ZendMvcControllerAbstractActionController; 2 3 4 class CallbackController extends AbstractActionController { 5 6 7 8 9 10 11 12 13 public function indexAction() { $storage = new ZendFeedPubSubHubbubModelSubscription; $callback = new ZendFeedPubSubHubbubSubscriberCallback; $callback->setStorage($storage); /** * Inject subscription key parsing from URL path using * a parameter from Router. 104.4. ZendFeedPubSubHubbubSubscriber 445
  • 484. Zend Framework 2 Documentation, Release 2.2.5 */ $subscriptionKey = $this->params()->fromRoute(’subkey’); $callback->setSubscriptionKey($subscriptionKey); $callback->handle(); $callback->sendResponse(); 14 15 16 17 18 19 /** * Check if the callback resulting in the receipt of a feed update. * Otherwise it was either a (un)sub verification request or invalid * request. Typically we need do nothing other than add feed update * handling - the rest is handled internally by the class. */ if ($callback->hasFeedUpdate()) { $feedString = $callback->getFeedUpdate(); /** * Process the feed update asynchronously to avoid a Hub timeout. */ } 20 21 22 23 24 25 26 27 28 29 30 31 } 32 33 34 } Actually adding the route which would map the path-appended key to a parameter for retrieval from a controller can be accomplished using a Route like in the example below. 1 2 3 4 5 6 7 8 9 10 11 // Callback Route to enable appending a PuSH Subscription’s lookup key $route = ZendMvcRouterHttpSegment::factory(array( ’route’ => ’/callback/:subkey’, ’constraints’ => array( ’subkey’ => ’[a-z0-9]+’ ), ’defaults’ => array( ’controller’ => ’application-index’, ’action’ => ’index’ ) )); 446 Chapter 104. ZendFeedPubSubHubbub
  • 485. CHAPTER 105 ZendFileClassFileLocator 105.1 Overview TODO 105.2 Available Methods TODO 105.3 Examples TODO 447
  • 486. Zend Framework 2 Documentation, Release 2.2.5 448 Chapter 105. ZendFileClassFileLocator
  • 487. CHAPTER 106 Introduction to ZendFilter The ZendFilter component provides a set of commonly needed data filters. It also provides a simple filter chaining mechanism by which multiple filters may be applied to a single datum in a user-defined order. 106.1 What is a filter? In the physical world, a filter is typically used for removing unwanted portions of input, and the desired portion of the input passes through as filter output (e.g., coffee). In such scenarios, a filter is an operator that produces a subset of the input. This type of filtering is useful for web applications - removing illegal input, trimming unnecessary white space, etc. This basic definition of a filter may be extended to include generalized transformations upon input. A common transformation applied in web applications is the escaping of HTML entities. For example, if a form field is automatically populated with untrusted input (e.g., from a web browser), this value should either be free of HTML entities or contain only escaped HTML entities, in order to prevent undesired behavior and security vulnerabilities. To meet this requirement, HTML entities that appear in the input must either be removed or escaped. Of course, which approach is more appropriate depends on the situation. A filter that removes the HTML entities operates within the scope of the first definition of filter - an operator that produces a subset of the input. A filter that escapes the HTML entities, however, transforms the input (e.g., “&” is transformed to “&amp;”). Supporting such use cases for web developers is important, and “to filter,” in the context of using ZendFilter, means to perform some transformations upon input data. 106.2 Basic usage of filters Having this filter definition established provides the foundation for ZendFilterFilterInterface, which requires a single method named filter() to be implemented by a filter class. Following is a basic example of using a filter upon two input data, the ampersand (&) and double quote (“) characters: 1 $htmlEntities = new ZendFilterHtmlEntities(); 2 3 4 echo $htmlEntities->filter(’&’); // &amp; echo $htmlEntities->filter(’"’); // &quot; Also, if a Filter inherits from ZendFilterAbstractFilter (just like all out-of-the-box Filters) you can also use them as such: 449
  • 488. Zend Framework 2 Documentation, Release 2.2.5 1 $strtolower = new ZendFilterStringToLower; 2 3 4 echo $strtolower(’I LOVE ZF2!’); // i love zf2! $zf2love = $strtolower(’I LOVE ZF2!’); 450 Chapter 106. Introduction to ZendFilter
  • 489. CHAPTER 107 Using the StaticFilter If it is inconvenient to load a given filter class and create an instance of the filter, you can use StaticFilter with it’s method execute() as an alternative invocation style. The first argument of this method is a data input value, that you would pass to the filter() method. The second argument is a string, which corresponds to the basename of the filter class, relative to the ZendFilter namespace. The execute() method automatically loads the class, creates an instance, and applies the filter() method to the data input. 1 echo StaticFilter::execute(’&’, ’HtmlEntities’); You can also pass an array of constructor arguments, if they are needed for the filter class. 1 2 3 echo StaticFilter::execute(’"’, ’HtmlEntities’, array(’quotestyle’ => ENT_QUOTES)); The static usage can be convenient for invoking a filter ad hoc, but if you have the need to run a filter for multiple inputs, it’s more efficient to follow the first example above, creating an instance of the filter object and calling its filter() method. Also, the FilterChain class allows you to instantiate and run multiple filter and validator classes on demand to process sets of input data. See FilterChain. You can set and receive the FilterPluginManager for the StaticFilter to amend the standard filter classes. 1 2 3 $pluginManager = StaticFilter::getPluginManager()->setInvokableClass( ’myNewFilter’, ’MyCustomFilterMyNewFilter’ ); 4 5 StaticFilter::setPluginManager(new MyFilterPluginManager()); This is useful when adding custom filters to be used by the StaticFilter. 107.1 Double filtering When using two filters after each other you have to keep in mind that it is often not possible to get the original output by using the opposite filter. Take the following example: 1 $original = "my_original_content"; 2 451
  • 490. Zend Framework 2 Documentation, Release 2.2.5 3 4 5 // Attach a filter $filter = new ZendFilterWordUnderscoreToCamelCase(); $filtered = $filter->filter($original); 6 7 8 9 // Use it’s opposite $filter2 = new ZendFilterWordCamelCaseToUnderscore(); $filtered = $filter2->filter($filtered) The above code example could lead to the impression that you will get the original output after the second filter has been applied. But thinking logically this is not the case. After applying the first filter my_original_content will be changed to MyOriginalContent. But after applying the second filter the result is My_Original_Content. As you can see it is not always possible to get the original output by using a filter which seems to be the opposite. It depends on the filter and also on the given input. 452 Chapter 107. Using the StaticFilter
  • 491. CHAPTER 108 Standard Filter Classes Zend Framework comes with a standard set of filters, which are ready for you to use. 108.1 Alnum The Alnum filter can be used to return only alphabetic characters and digits in the unicode “letter” and “number” categories, respectively. All other characters are suppressed. Supported Options for Alnum Filter The following options are supported for Alnum: Alnum([ boolean $allowWhiteSpace [, string $locale ]]) • $allowWhiteSpace: If set to true then whitespace characters are allowed. Otherwise they are suppressed. Default is “false” (whitespace is not allowed). Methods for getting/setting the allowWhiteSpace option are also available: getAllowWhiteSpace() and setAllowWhiteSpace() • $locale: The locale string used in identifying the characters to filter (locale name, e.g. en_US). If unset, it will use the default locale (Locale::getDefault()). Methods for getting/setting the locale are also available: getLocale() and setLocale() Alnum Filter Usage 1 2 3 4 // Default settings, deny whitespace $filter = new ZendI18nFilterAlnum(); echo $filter->filter("This is (my) content: 123"); // Returns "Thisismycontent123" 5 6 7 8 9 // First param in constructor is $allowWhiteSpace $filter = new ZendI18nFilterAlnum(true); echo $filter->filter("This is (my) content: 123"); // Returns "This is my content 123" 453
  • 492. Zend Framework 2 Documentation, Release 2.2.5 Note: Alnum works on almost all languages, except: Chinese, Japanese and Korean. Within these languages the english alphabet is used instead of the characters from these languages. The language itself is detected using the Locale. 108.2 Alpha The Alpha filter can be used to return only alphabetic characters in the unicode “letter” category. All other characters are suppressed. Supported Options for Alpha Filter The following options are supported for Alpha: Alpha([ boolean $allowWhiteSpace [, string $locale ]]) • $allowWhiteSpace: If set to true then whitespace characters are allowed. Otherwise they are suppressed. Default is “false” (whitespace is not allowed). Methods for getting/setting the allowWhiteSpace option are also available: getAllowWhiteSpace() and setAllowWhiteSpace() • $locale: The locale string used in identifying the characters to filter (locale name, e.g. en_US). If unset, it will use the default locale (Locale::getDefault()). Methods for getting/setting the locale are also available: getLocale() and setLocale() Alpha Filter Usage 1 2 3 4 // Default settings, deny whitespace $filter = new ZendI18nFilterAlpha(); echo $filter->filter("This is (my) content: 123"); // Returns "Thisismycontent" 5 6 7 8 9 // Allow whitespace $filter = new ZendI18nFilterAlpha(true); echo $filter->filter("This is (my) content: 123"); // Returns "This is my content " Note: Alpha works on almost all languages, except: Chinese, Japanese and Korean. Within these languages the english alphabet is used instead of the characters from these languages. The language itself is detected using the Locale. 108.3 BaseName ZendFilterBaseName allows you to filter a string which contains the path to a file and it will return the base name of this file. 454 Chapter 108. Standard Filter Classes
  • 493. Zend Framework 2 Documentation, Release 2.2.5 Supported Options There are no additional options for ZendFilterBaseName. Basic Usage A basic example of usage is below: 1 $filter = new ZendFilterBaseName(); 2 3 print $filter->filter(’/vol/tmp/filename’); This will return ‘filename’. 1 $filter = new ZendFilterBaseName(); 2 3 print $filter->filter(’/vol/tmp/filename.txt’); This will return ‘filename.txt‘. 108.4 Boolean This filter changes a given input to be a BOOLEAN value. This is often useful when working with databases or when processing form values. Supported Options The following options are supported for ZendFilterBoolean: • casting: When this option is set to TRUE then any given input will be casted to boolean. This option defaults to TRUE. • locale: This option sets the locale which will be used to detect localized input. • type: The type option sets the boolean type which should be used. Read the following for details. Default Behavior By default, this filter works by casting the input to a BOOLEAN value; in other words, it operates in a similar fashion to calling (boolean) $value. 1 2 3 4 $filter = new ZendFilterBoolean(); $value = ’’; $result = $filter->filter($value); // returns false This means that without providing any configuration, ZendFilterBoolean accepts all input types and returns a BOOLEAN just as you would get by type casting to BOOLEAN. 108.4. Boolean 455
  • 494. Zend Framework 2 Documentation, Release 2.2.5 Changing the Default Behavior Sometimes casting with (boolean) will not suffice. ZendFilterBoolean allows you to configure specific types to convert, as well as which to omit. The following types can be handled: • boolean: Returns a boolean value as is. • integer: Converts an integer 0 value to FALSE. • float: Converts a float 0.0 value to FALSE. • string: Converts an empty string ‘’ to FALSE. • zero: Converts a string containing the single character zero (‘0’) to FALSE. • empty_array: Converts an empty array to FALSE. • null: Converts a NULL value to FALSE. • php: Converts values according to PHP when casting them to BOOLEAN. • false_string: Converts a string containing the word “false” to a boolean FALSE. • yes: Converts a localized string which contains the word “no” to FALSE. • all: Converts all above types to BOOLEAN. All other given values will return TRUE by default. There are several ways to select which of the above types are filtered. You can give one or multiple types and add them, you can give an array, you can use constants, or you can give a textual string. See the following examples: 1 2 // converts 0 to false $filter = new ZendFilterBoolean(ZendFilterBoolean::INTEGER); 3 4 5 6 7 // converts 0 and ’0’ to false $filter = new ZendFilterBoolean( ZendFilterBoolean::INTEGER + ZendFilterBoolean::ZERO ); 8 9 10 11 12 13 14 15 // converts 0 and ’0’ to false $filter = new ZendFilterBoolean(array( ’type’ => array( ZendFilterBoolean::INTEGER, ZendFilterBoolean::ZERO, ), )); 16 17 18 19 20 21 22 23 // converts 0 and ’0’ to false $filter = new ZendFilterBoolean(array( ’type’ => array( ’integer’, ’zero’, ), )); You can also give an instance of ZendConfigConfig to set the desired types. To set types after instantiation, use the setType() method. 456 Chapter 108. Standard Filter Classes
  • 495. Zend Framework 2 Documentation, Release 2.2.5 Localized Booleans As mentioned previously, ZendFilterBoolean can also recognise localized “yes” and “no” strings. This means that you can ask your customer in a form for “yes” or “no” within his native language and ZendFilterBoolean will convert the response to the appropriate boolean value. To set the desired locale, you can either use the locale option, or the method setLocale(). 1 2 3 4 $filter = new ZendFilterBoolean(array( ’type’ => ZendFilterBoolean::ALL, ’locale’ => ’de’, )); 5 6 7 // returns false echo $filter->filter(’nein’); 8 9 $filter->setLocale(’en’); 10 11 12 // returns true $filter->filter(’yes’); Disable Casting Sometimes it is necessary to recognise only TRUE or FALSE and return all other values without changes. ZendFilterBoolean allows you to do this by setting the casting option to FALSE. In this case ZendFilterBoolean will work as described in the following table, which shows which values return TRUE or FALSE. All other given values are returned without change when casting is set to FALSE Table 108.1: Usage without casting Type ZendFilterBoolean::BOOLEAN ZendFilterBoolean::INTEGER ZendFilterBoolean::FLOAT ZendFilterBoolean::STRING ZendFilterBoolean::ZERO ZendFilterBoolean::EMPTY_ARRAY ZendFilterBoolean::NULL ZendFilterBoolean::FALSE_STRING ZendFilterBoolean::YES True TRUE 0 0.0 “” “0” array() NULL “false” (case independently) localized “yes” (case independently) False FALSE 1 1.0 “1” “true” (case independently) localized “no” (case independently) The following example shows the behaviour when changing the casting option: 1 2 3 4 $filter = new ZendFilterBoolean(array( ’type’ => ZendFilterBoolean::ALL, ’casting’ => false, )); 5 6 7 // returns false echo $filter->filter(0); 8 9 10 // returns true echo $filter->filter(1); 11 12 13 // returns the value echo $filter->filter(2); 108.4. Boolean 457
  • 496. Zend Framework 2 Documentation, Release 2.2.5 108.5 Callback This filter allows you to use own methods in conjunction with ZendFilter. You don’t have to create a new filter when you already have a method which does the job. Supported Options The following options are supported for ZendFilterCallback: • callback: This sets the callback which should be used. • options: This property sets the options which are used when the callback is processed. Basic Usage The usage of this filter is quite simple. Let’s expect we want to create a filter which reverses a string. 1 $filter = new ZendFilterCallback(’strrev’); 2 3 4 print $filter->filter(’Hello!’); // returns "!olleH" As you can see it’s really simple to use a callback to define a own filter. It is also possible to use a method, which is defined within a class, by giving an array as callback. 1 2 3 4 5 // Our classdefinition class MyClass { public function Reverse($param); } 6 7 8 9 // The filter definition $filter = new ZendFilterCallback(array(’MyClass’, ’Reverse’)); print $filter->filter(’Hello!’); To get the actual set callback use getCallback() and to set another callback use setCallback(). Note: Possible exceptions You should note that defining a callback method which can not be called will raise an exception. Default Parameters Within a Callback It is also possible to define default parameters, which are given to the called method as array when the filter is executed. This array will be concatenated with the value which will be filtered. 1 2 3 4 5 6 7 $filter = new ZendFilterCallback( array( ’callback’ => ’MyMethod’, ’options’ => array(’key’ => ’param1’, ’key2’ => ’param2’) ) ); $filter->filter(array(’value’ => ’Hello’)); 458 Chapter 108. Standard Filter Classes
  • 497. Zend Framework 2 Documentation, Release 2.2.5 When you would call the above method definition manually it would look like this: 1 $value = MyMethod(’Hello’, ’param1’, ’param2’); 108.6 Compress and Decompress These two filters are capable of compressing and decompressing strings, files, and directories. Supported Options The following options are supported for ZendFilterCompress and ZendFilterDecompress: • adapter: The compression adapter which should be used. It defaults to Gz. • options: Additional options which are given to the adapter at initiation. Each adapter supports it’s own options. Supported Compression Adapters The following compression formats are supported by their own adapter: • Bz2 • Gz • Lzf • Rar • Tar • Zip Each compression format has different capabilities as described below. All compression filters may be used in approximately the same ways, and differ primarily in the options available and the type of compression they offer (both algorithmically as well as string vs. file vs. directory) Generic Handling To create a compression filter you need to select the compression format you want to use. The following description takes the Bz2 adapter. Details for all other adapters are described after this section. The two filters are basically identical, in that they utilize the same backends. ZendFilterCompress should be used when you wish to compress items, and ZendFilterDecompress should be used when you wish to decompress items. For instance, if we want to compress a string, we have to initiate ZendFilterCompress and indicate the desired adapter. 1 $filter = new ZendFilterCompress(’Bz2’); To use a different adapter, you simply specify it to the constructor. You may also provide an array of options or a Traversable object. If you do, provide minimally the key “adapter”, and then either the key “options” or “adapterOptions” (which should be an array of options to provide to the adapter on instantiation). 108.6. Compress and Decompress 459
  • 498. Zend Framework 2 Documentation, Release 2.2.5 1 2 3 4 5 6 $filter = new ZendFilterCompress(array( ’adapter’ => ’Bz2’, ’options’ => array( ’blocksize’ => 8, ), )); Note: Default compression Adapter When no compression adapter is given, then the Gz adapter will be used. Almost the same usage is we want to decompress a string. We just have to use the decompression filter in this case. 1 $filter = new ZendFilterDecompress(’Bz2’); To get the compressed string, we have to give the original string. The filtered value is the compressed version of the original string. 1 2 3 $filter = new ZendFilterCompress(’Bz2’); $compressed = $filter->filter(’Uncompressed string’); // Returns the compressed string Decompression works the same way. 1 2 3 $filter = new ZendFilterDecompress(’Bz2’); $compressed = $filter->filter(’Compressed string’); // Returns the uncompressed string Note: Note on string compression Not all adapters support string compression. Compression formats like Rar can only handle files and directories. For details, consult the section for the adapter you wish to use. Creating an Archive Creating an archive file works almost the same as compressing a string. However, in this case we need an additional parameter which holds the name of the archive we want to create. 1 2 3 4 5 6 7 8 $filter = new ZendFilterCompress(array( ’adapter’ => ’Bz2’, ’options’ => array( ’archive’ => ’filename.bz2’, ), )); $compressed = $filter->filter(’Uncompressed string’); // Returns true on success and creates the archive file In the above example the uncompressed string is compressed, and is then written into the given archive file. Note: Existing archives will be overwritten The content of any existing file will be overwritten when the given filename of the archive already exists. When you want to compress a file, then you must give the name of the file with its path. 460 Chapter 108. Standard Filter Classes
  • 499. Zend Framework 2 Documentation, Release 2.2.5 1 2 3 4 5 6 7 8 $filter = new ZendFilterCompress(array( ’adapter’ => ’Bz2’, ’options’ => array( ’archive’ => ’filename.bz2’ ), )); $compressed = $filter->filter(’C:tempcompressme.txt’); // Returns true on success and creates the archive file You may also specify a directory instead of a filename. In this case the whole directory with all its files and subdirectories will be compressed into the archive. 1 2 3 4 5 6 7 8 $filter = new ZendFilterCompress(array( ’adapter’ => ’Bz2’, ’options’ => array( ’archive’ => ’filename.bz2’ ), )); $compressed = $filter->filter(’C:tempsomedir’); // Returns true on success and creates the archive file Note: Do not compress large or base directories You should never compress large or base directories like a complete partition. Compressing a complete partition is a very time consuming task which can lead to massive problems on your server when there is not enough space or your script takes too much time. Decompressing an Archive Decompressing an archive file works almost like compressing it. You must specify either the archive parameter, or give the filename of the archive when you decompress the file. 1 2 3 $filter = new ZendFilterDecompress(’Bz2’); $decompressed = $filter->filter(’filename.bz2’); // Returns true on success and decompresses the archive file Some adapters support decompressing the archive into another subdirectory. In this case you can set the target parameter. 1 2 3 4 5 6 7 8 9 $filter = new ZendFilterDecompress(array( ’adapter’ => ’Zip’, ’options’ => array( ’target’ => ’C:temp’, ) )); $decompressed = $filter->filter(’filename.zip’); // Returns true on success and decompresses the archive file // into the given target directory Note: Directories to extract to must exist When you want to decompress an archive into a directory, then that directory must exist. 108.6. Compress and Decompress 461
  • 500. Zend Framework 2 Documentation, Release 2.2.5 Bz2 Adapter The Bz2 Adapter can compress and decompress: • Strings • Files • Directories This adapter makes use of PHP‘s Bz2 extension. To customize compression, this adapter supports the following options: • Archive: This parameter sets the archive file which should be used or created. • Blocksize: This parameter sets the blocksize to use. It can be from ‘0’ to ‘9’. The default value is ‘4’. All options can be set at instantiation or by using a related method. For example, the related methods for ‘Blocksize’ are getBlocksize() and setBlocksize(). You can also use the setOptions() method which accepts all options as array. Gz Adapter The Gz Adapter can compress and decompress: • Strings • Files • Directories This adapter makes use of PHP‘s Zlib extension. To customize the compression this adapter supports the following options: • Archive: This parameter sets the archive file which should be used or created. • Level: This compression level to use. It can be from ‘0’ to ‘9’. The default value is ‘9’. • Mode: There are two supported modes. ‘compress’ and ‘deflate’. The default value is ‘compress’. All options can be set at initiation or by using a related method. For example, the related methods for ‘Level’ are getLevel() and setLevel(). You can also use the setOptions() method which accepts all options as array. Lzf Adapter The Lzf Adapter can compress and decompress: • Strings Note: Lzf supports only strings The Lzf adapter can not handle files and directories. This adapter makes use of PHP‘s Lzf extension. There are no options available to customize this adapter. 462 Chapter 108. Standard Filter Classes
  • 501. Zend Framework 2 Documentation, Release 2.2.5 Rar Adapter The Rar Adapter can compress and decompress: • Files • Directories Note: Rar does not support strings The Rar Adapter can not handle strings. This adapter makes use of PHP‘s Rar extension. Note: Rar compression not supported Due to restrictions with the Rar compression format, there is no compression available for free. When you want to compress files into a new Rar archive, you must provide a callback to the adapter that can invoke a Rar compression program. To customize the compression this adapter supports the following options: • Archive: This parameter sets the archive file which should be used or created. • Callback: A callback which provides compression support to this adapter. • Password: The password which has to be used for decompression. • Target: The target where the decompressed files will be written to. All options can be set at instantiation or by using a related method. For example, the related methods for ‘Target’ are getTarget() and setTarget(). You can also use the setOptions() method which accepts all options as array. Tar Adapter The Tar Adapter can compress and decompress: • Files • Directories Note: Tar does not support strings The Tar Adapter can not handle strings. This adapter makes use of PEAR‘s Archive_Tar component. To customize the compression this adapter supports the following options: • Archive: This parameter sets the archive file which should be used or created. • Mode: A mode to use for compression. Supported are either ‘NULL‘ which means no compression at all, ‘Gz’ which makes use of PHP‘s Zlib extension and ‘Bz2’ which makes use of PHP‘s Bz2 extension. The default value is ‘NULL‘. • Target: The target where the decompressed files will be written to. 108.6. Compress and Decompress 463
  • 502. Zend Framework 2 Documentation, Release 2.2.5 All options can be set at instantiation or by using a related method. For example, the related methods for ‘Target’ are getTarget() and setTarget(). You can also use the setOptions() method which accepts all options as array. Note: Directory usage When compressing directories with Tar then the complete file path is used. This means that created Tar files will not only have the subdirectory but the complete path for the compressed file. Zip Adapter The Zip Adapter can compress and decompress: • Strings • Files • Directories Note: Zip does not support string decompression The Zip Adapter can not handle decompression to a string; decompression will always be written to a file. This adapter makes use of PHP‘s Zip extension. To customize the compression this adapter supports the following options: • Archive: This parameter sets the archive file which should be used or created. • Target: The target where the decompressed files will be written to. All options can be set at instantiation or by using a related method. For example, the related methods for ‘Target’ are getTarget() and setTarget(). You can also use the setOptions() method which accepts all options as array. 108.7 Digits Returns the string $value, removing all but digits. Supported Options There are no additional options for ZendFilterDigits. Basic Usage A basic example of usage is below: 1 $filter = new ZendFilterDigits(); 2 3 print $filter->filter(’October 2012’); This returns “2012”. 464 Chapter 108. Standard Filter Classes
  • 503. Zend Framework 2 Documentation, Release 2.2.5 1 $filter = new ZendFilterDigits(); 2 3 print $filter->filter(’HTML 5 for Dummies’); This returns “5”. 108.8 Dir Given a string containing a path to a file, this function will return the name of the directory. Supported Options There are no additional options for ZendFilterDir. Basic Usage A basic example of usage is below: 1 $filter = new ZendFilterDir(); 2 3 print $filter->filter(’/etc/passwd’); This returns “/etc”. 1 $filter = new ZendFilterDir(); 2 3 print $filter->filter(’C:/Temp/x’); This returns “C:/Temp”. 108.9 Encrypt and Decrypt These filters allow to encrypt and decrypt any given string. Therefor they make use of Adapters. Actually there are adapters for the ZendCryptBlockCipher class and the OpenSSL extension of PHP. Supported Options The following options are supported for ZendFilterEncrypt and ZendFilterDecrypt: • adapter: This sets the encryption adapter which should be used • algorithm: Only BlockCipher. The algorithm which has to be used by the adapter ZendCryptSymmetricMcrypt. It should be one of the algorithm ciphers supported by ZendCryptSymmetricMcrypt (see the getSupportedAlgorithms() method). If not set it defaults to aes, the Advanced Encryption Standard (see ZendCryptBlockCipher for more details). • compression: If the encrypted value should be compressed. Default is no compression. • envelope: Only OpenSSL. The encrypted envelope key from the user who encrypted the content. You can either provide the path and filename of the key file, or just the content of the key file itself. When the package option has been set, then you can omit this parameter. 108.8. Dir 465
  • 504. Zend Framework 2 Documentation, Release 2.2.5 • key: Only BlockCipher. The encryption key with which the input will be encrypted. You need the same key for decryption. • mode: Only BlockCipher. The encryption mode which has to be used. It should be one of the modes which can be found under PHP’s mcrypt modes. If not set it defaults to ‘cbc’. • mode_directory: Only BlockCipher. The directory where the mode can be found. If not set it defaults to the path set within the Mcrypt extension. • package: Only OpenSSL. If the envelope key should be packed with the encrypted value. Default is FALSE. • private: Only OpenSSL. Your private key which will be used for encrypting the content. Also the private key can be either a filename with path of the key file, or just the content of the key file itself. • public: Only OpenSSL. The public key of the user whom you want to provide the encrypted content. You can give multiple public keys by using an array. You can either provide the path and filename of the key file, or just the content of the key file itself. • vector: Only BlockCipher. The initialization vector which shall be used. If not set it will be a random vector. Adapter Usage As these two encryption methodologies work completely different, also the usage of the adapters differ. You have to select the adapter you want to use when initiating the filter. 1 2 // Use the BlockCipher adapter $filter1 = new ZendFilterEncrypt(array(’adapter’ => ’BlockCipher’)); 3 4 5 // Use the OpenSSL adapter $filter2 = new ZendFilterEncrypt(array(’adapter’ => ’openssl’)); To set another adapter you can also use setAdapter(), and the getAdapter() method to receive the actual set adapter. 1 2 3 // Use the OpenSSL adapter $filter = new ZendFilterEncrypt(); $filter->setAdapter(’openssl’); Note: When you do not supply the adapter option or do not use setAdapter(), then the BlockCipher adapter will be used per default. Encryption with BlockCipher To encrypt a string using the BlockCipher you have to specify the encryption key using the setKey() method or passing it during the constructor. 1 2 3 // Use the default AES encryption algorithm $filter = new ZendFilterEncrypt(array(’adapter’ => ’BlockCipher’)); $filter->setKey(’encryption key’); 4 5 6 7 8 9 // or // $filter = new ZendFilterEncrypt(array( // ’adapter’ => ’BlockCipher’, // ’key’ => ’encryption key’ // )); 466 Chapter 108. Standard Filter Classes
  • 505. Zend Framework 2 Documentation, Release 2.2.5 10 11 12 $encrypted = $filter->filter(’text to be encrypted’); printf ("Encrypted text: %sn", $encrypted); You can get and set the encryption values also afterwards with the getEncryption() and setEncryption() methods. 1 2 3 4 // Use the default AES encryption algorithm $filter = new ZendFilterEncrypt(array(’adapter’ => ’BlockCipher’)); $filter->setKey(’encryption key’); var_dump($filter->getEncryption()); 5 6 7 8 9 10 11 12 13 14 15 16 // Will print: //array(4) { // ["key_iteration"]=> // int(5000) // ["algorithm"]=> // string(3) "aes" // ["hash"]=> // string(6) "sha256" // ["key"]=> // string(14) "encryption key" //} Note: The BlockCipher adapter uses the Mcrypt PHP extension by default. That means you will need to install the Mcrypt module in your PHP environment. If you don’t specify an initialization Vector (salt or iv), the BlockCipher will generate a random value during each encryption. If you try to execute the following code the output will be always different (note that even if the output is always different you can decrypt it using the same key). 1 2 $key = ’encryption key’; $text = ’message to encrypt’; 3 4 5 6 7 8 9 // use the default adapter that is BlockCipher $filter = new ZendFilterEncrypt(); $filter->setKey(’encryption key’); for ($i=0; $i < 10; $i++) { printf("%d) %sn", $i, $filter->filter($text)); } If you want to obtain the same output you need to specify a fixed Vector, using the setVector() method. This script will produce always the same encryption output. 1 2 3 4 5 // use the default adapter that is BlockCipher $filter = new ZendFilterEncrypt(); $filter->setKey(’encryption key’); $filter->setVector(’12345678901234567890’); printf("%sn", $filter->filter(’message’)); 6 7 8 // output: // 04636a6cb8276fad0787a2e187803b6557f77825d5ca6ed4392be702b9754bb3MTIzNDU2Nzg5MDEyMzQ1NgZ+zPwTGpV6gQ Note: For a security reason it’s always better to use a different Vector on each encryption. We suggest to use the setVector() method only if you really need it. 108.9. Encrypt and Decrypt 467
  • 506. Zend Framework 2 Documentation, Release 2.2.5 Decryption with BlockCipher For decrypting content which was previously encrypted with BlockCipher you need to have the options with which the encryption has been called. If you used only the encryption key, you can just use it to decrypt the content. As soon as you have provided all options decryption is as simple as encryption. 1 2 3 4 5 $content = ’04636a6cb8276fad0787a2e187803b6557f77825d5ca6ed4392be702b9754bb3MTIzNDU2Nzg5MDEyMzQ1NgZ+z // use the default adapter that is BlockCipher $filter = new ZendFilterDecrypt(); $filter->setKey(’encryption key’); printf("Decrypt: %sn", $filter->filter($content)); 6 7 8 // output: // Decrypt: message Note that even if we did not specify the same Vector, the BlockCipher is able to decrypt the message because the Vector is stored in the encryption string itself (note that the Vector can be stored in plaintext, it is not a secret, the Vector is only used to improve the randomness of the encryption algorithm). Note: You should also note that all settings which be checked when you create the instance or when you call setEncryption(). Encryption with OpenSSL When you have installed the OpenSSL extension you can use the OpenSSL adapter. You can get or set the public keys also afterwards with the getPublicKey() and setPublicKey() methods. The private key can also be get and set with the related getPrivateKey() and setPrivateKey() methods. 1 2 3 4 5 // Use openssl and provide a private key $filter = new ZendFilterEncrypt(array( ’adapter’ => ’openssl’, ’private’ => ’/path/to/mykey/private.pem’ )); 6 7 8 9 10 11 // of course you can also give the public keys at initiation $filter->setPublicKey(array( ’/public/key/path/first.pem’, ’/public/key/path/second.pem’ )); Note: Note that the OpenSSL adapter will not work when you do not provide valid keys. When you want to encode also the keys, then you have to provide a passphrase with the setPassphrase() method. When you want to decode content which was encoded with a passphrase you will not only need the public key, but also the passphrase to decode the encrypted key. 1 2 3 4 5 // Use openssl and provide a private key $filter = new ZendFilterEncrypt(array( ’adapter’ => ’openssl’, ’private’ => ’/path/to/mykey/private.pem’ )); 6 468 Chapter 108. Standard Filter Classes
  • 507. Zend Framework 2 Documentation, Release 2.2.5 7 8 9 10 11 12 // of course you can also give the public keys at initiation $filter->setPublicKey(array( ’/public/key/path/first.pem’, ’/public/key/path/second.pem’ )); $filter->setPassphrase(’mypassphrase’); At last, when you use OpenSSL you need to give the receiver the encrypted content, the passphrase when have provided one, and the envelope keys for decryption. This means for you, that you have to get the envelope keys after the encryption with the getEnvelopeKey() method. So our complete example for encrypting content with OpenSSL look like this. 1 2 3 4 5 // Use openssl and provide a private key $filter = new ZendFilterEncrypt(array( ’adapter’ => ’openssl’, ’private’ => ’/path/to/mykey/private.pem’ )); 6 7 8 9 10 11 12 // of course you can also give the public keys at initiation $filter->setPublicKey(array( ’/public/key/path/first.pem’, ’/public/key/path/second.pem’ )); $filter->setPassphrase(’mypassphrase’); 13 14 15 16 $encrypted = $filter->filter(’text_to_be_encoded’); $envelope = $filter->getEnvelopeKey(); print $encrypted; 17 18 // For decryption look at the Decrypt filter Simplified usage with Openssl As seen before, you need to get the envelope key to be able to decrypt the previous encrypted value. This can be very annoying when you work with multiple values. To have a simplified usage you can set the package option to TRUE. The default value is FALSE. 1 2 3 4 5 6 7 // Use openssl and provide a private key $filter = new ZendFilterEncrypt(array( ’adapter’ => ’openssl’, ’private’ => ’/path/to/mykey/private.pem’, ’public’ => ’/public/key/path/public.pem’, ’package’ => true )); 8 9 10 $encrypted = $filter->filter(’text_to_be_encoded’); print $encrypted; 11 12 // For decryption look at the Decrypt filter Now the returned value contains the encrypted value and the envelope. You don’t need to get them after the compression. But, and this is the negative aspect of this feature, the encrypted value can now only be decrypted by using ZendFilterEncrypt. 108.9. Encrypt and Decrypt 469
  • 508. Zend Framework 2 Documentation, Release 2.2.5 Compressing Content Based on the original value, the encrypted value can be a very large string. ZendFilterEncrypt allows the usage of compression. To reduce the value The compression option can either be set to the name of a compression adapter, or to an array which sets all wished options for the compression adapter. 1 2 3 4 5 6 7 8 // Use basic compression adapter $filter1 = new ZendFilterEncrypt(array( ’adapter’ => ’openssl’, ’private’ => ’/path/to/mykey/private.pem’, ’public’ => ’/public/key/path/public.pem’, ’package’ => true, ’compression’ => ’bz2’ )); 9 10 11 12 13 14 15 16 17 // Use basic compression adapter $filter2 = new ZendFilterEncrypt(array( ’adapter’ => ’openssl’, ’private’ => ’/path/to/mykey/private.pem’, ’public’ => ’/public/key/path/public.pem’, ’package’ => true, ’compression’ => array(’adapter’ => ’zip’, ’target’ => ’usrtmptmp.zip’) )); Note: Decryption with same settings When you want to decrypt a value which is additionally compressed, then you need to set the same compression settings for decryption as for encryption. Otherwise the decryption will fail. Decryption with OpenSSL Decryption with OpenSSL is as simple as encryption. But you need to have all data from the person who encrypted the content. See the following example: 1 2 3 4 5 // Use openssl and provide a private key $filter = new ZendFilterDecrypt(array( ’adapter’ => ’openssl’, ’private’ => ’/path/to/mykey/private.pem’ )); 6 7 8 9 10 11 // of course you can also give the envelope keys at initiation $filter->setEnvelopeKey(array( ’/key/from/encoder/first.pem’, ’/key/from/encoder/second.pem’ )); Note: Note that the OpenSSL adapter will not work when you do not provide valid keys. Optionally it could be necessary to provide the passphrase for decrypting the keys themself by using the setPassphrase() method. 1 2 // Use openssl and provide a private key $filter = new ZendFilterDecrypt(array( 470 Chapter 108. Standard Filter Classes
  • 509. Zend Framework 2 Documentation, Release 2.2.5 3 4 5 ’adapter’ => ’openssl’, ’private’ => ’/path/to/mykey/private.pem’ )); 6 7 8 9 10 11 12 // of course you can also give the envelope keys at initiation $filter->setEnvelopeKey(array( ’/key/from/encoder/first.pem’, ’/key/from/encoder/second.pem’ )); $filter->setPassphrase(’mypassphrase’); At last, decode the content. Our complete example for decrypting the previously encrypted content looks like this. 1 2 3 4 5 // Use openssl and provide a private key $filter = new ZendFilterDecrypt(array( ’adapter’ => ’openssl’, ’private’ => ’/path/to/mykey/private.pem’ )); 6 7 8 9 10 11 12 // of course you can also give the envelope keys at initiation $filter->setEnvelopeKey(array( ’/key/from/encoder/first.pem’, ’/key/from/encoder/second.pem’ )); $filter->setPassphrase(’mypassphrase’); 13 14 15 $decrypted = $filter->filter(’encoded_text_normally_unreadable’); print $decrypted; 108.10 HtmlEntities Returns the string $value, converting characters to their corresponding HTML entity equivalents where they exist. Supported Options The following options are supported for ZendFilterHtmlEntities: • quotestyle: Equivalent to the PHP htmlentities native function parameter quote_style. This allows you to define what will be done with ‘single’ and “double” quotes. The following constants are accepted: ENT_COMPAT, ENT_QUOTES ENT_NOQUOTES with the default being ENT_COMPAT. • charset: Equivalent to the PHP htmlentities native function parameter charset. This defines the character set to be used in filtering. Unlike the PHP native function the default is ‘UTF-8’. See “http://php.net/htmlentities” for a list of supported character sets. Note: This option can also be set via the $options parameter as a Traversable object or array. The option key will be accepted as either charset or encoding. • doublequote: Equivalent to the PHP htmlentities native function parameter double_encode. If set to false existing html entities will not be encoded. The default is to convert everything (true). Note: This option must be set via the $options parameter or the setDoubleEncode() method. 108.10. HtmlEntities 471
  • 510. Zend Framework 2 Documentation, Release 2.2.5 Basic Usage See the following example for the default behavior of this filter. 1 $filter = new ZendFilterHtmlEntities(); 2 3 print $filter->filter(’<’); Quote Style ZendFilterHtmlEntities allows changing the quote style used. This can be useful when you want to leave double, single, or both types of quotes un-filtered. See the following example: 1 $filter = new ZendFilterHtmlEntities(array(’quotestyle’ => ENT_QUOTES)); 2 3 4 $input = "A ’single’ and " . ’"double"’; print $filter->filter($input); The above example returns A &#039;single&#039; and &quot;double&quot;. Notice that ’single’ as well as "double" quotes are filtered. 1 $filter = new ZendFilterHtmlEntities(array(’quotestyle’ => ENT_COMPAT)); 2 3 4 $input = "A ’single’ and " . ’"double"’; print $filter->filter($input); The above example returns A ’single’ and &quot;double&quot;. Notice that "double" quotes are filtered while ’single’ quotes are not altered. 1 $filter = new ZendFilterHtmlEntities(array(’quotestyle’ => ENT_NOQUOTES)); 2 3 4 $input = "A ’single’ and " . ’"double"’; print $filter->filter($input); The above example returns A ’single’ and "double". Notice that neither "double" or ’single’ quotes are altered. Helper Methods To change or retrieve the quotestyle after instantiation, the two methods setQuoteStyle() and getQuoteStyle() may be used respectively. setQuoteStyle() accepts one parameter $quoteStyle. The following constants are accepted: ENT_COMPAT, ENT_QUOTES, ENT_NOQUOTES 1 $filter = new ZendFilterHtmlEntities(); 2 3 4 $filter->setQuoteStyle(ENT_QUOTES); print $filter->getQuoteStyle(ENT_QUOTES); To change or retrieve the charset after instantiation, the two methods setCharSet() and getCharSet() may be used respectively. setCharSet() accepts one parameter $charSet. See “http://php.net/htmlentities” for a list of supported character sets. 1 $filter = new ZendFilterHtmlEntities(); 2 3 4 $filter->setQuoteStyle(ENT_QUOTES); print $filter->getQuoteStyle(ENT_QUOTES); 472 Chapter 108. Standard Filter Classes
  • 511. Zend Framework 2 Documentation, Release 2.2.5 To change or retrieve the doublequote option after instantiation, the two methods setDoubleQuote() and getDoubleQuote() may be used respectively. setDoubleQuote() accepts one boolean parameter $doubleQuote. 1 $filter = new ZendFilterHtmlEntities(); 2 3 4 $filter->setQuoteStyle(ENT_QUOTES); print $filter->getQuoteStyle(ENT_QUOTES); 108.11 Int ZendFilterInt allows you to transform a scalar value which contains into an integer. Supported Options There are no additional options for ZendFilterInt. Basic Usage A basic example of usage is below: 1 $filter = new ZendFilterInt(); 2 3 print $filter->filter(’-4 is less than 0’); This will return ‘-4’. 108.12 Null This filter will change the given input to be NULL if it meets specific criteria. This is often necessary when you work with databases and want to have a NULL value instead of a boolean or any other type. Supported Options The following options are supported for ZendFilterNull: • type: The variable type which should be supported. Default Behavior Per default this filter works like PHP‘s empty() method; in other words, if empty() returns a boolean TRUE, then a NULL value will be returned. 1 2 3 4 $filter = new ZendFilterNull(); $value = ’’; $result = $filter->filter($value); // returns null instead of the empty string 108.11. Int 473
  • 512. Zend Framework 2 Documentation, Release 2.2.5 This means that without providing any configuration, ZendFilterNull will accept all input types and return NULL in the same cases as empty(). Any other value will be returned as is, without any changes. Changing the Default Behavior Sometimes it’s not enough to filter based on empty(). Therefor ZendFilterNull allows you to configure which type will be converted and which not. The following types can be handled: • boolean: Converts a boolean FALSE value to NULL. • integer: Converts an integer 0 value to NULL. • empty_array: Converts an empty array to NULL. • float: Converts an float 0.0 value to NULL. • string: Converts an empty string ‘’ to NULL. • zero: Converts a string containing the single character zero (‘0’) to NULL. • all: Converts all above types to NULL. (This is the default behavior.) There are several ways to select which of the above types are filtered. You can give one or multiple types and add them, you can give an array, you can use constants, or you can give a textual string. See the following examples: 1 2 // converts false to null $filter = new ZendFilterNull(ZendFilterNull::BOOLEAN); 3 4 5 6 7 // converts false and 0 to null $filter = new ZendFilterNull( ZendFilterNull::BOOLEAN + ZendFilterNull::INTEGER ); 8 9 10 11 12 13 // converts false and 0 to null $filter = new ZendFilterNull( array( ZendFilterNull::BOOLEAN, ZendFilterNull::INTEGER )); 14 15 16 17 18 19 // converts false and 0 to null $filter = new ZendFilterNull(array( ’boolean’, ’integer’, )); You can also give a Traversable or an array to set the wished types. To set types afterwards use setType(). 108.13 NumberFormat The NumberFormat filter can be used to return locale-specific number and percentage strings. It extends the NumberParse filter, which acts as wrapper for the NumberFormatter class within the Internationalization extension (Intl). 474 Chapter 108. Standard Filter Classes
  • 513. Zend Framework 2 Documentation, Release 2.2.5 Supported Options for NumberFormat Filter The following options are supported for NumberFormat: NumberFormat([ string $locale [, int $style [, int $type ]]]) • $locale: (Optional) Locale in which the number would be formatted (locale name, e.g. en_US). If unset, it will use the default locale (Locale::getDefault()) Methods for getting/setting the locale are also available: getLocale() and setLocale() • $style: (Optional) Style of the formatting, one of the format style constants. NumberFormatter::DEFAULT_STYLE as the default style. If unset, it will use Methods for getting/setting the format style are also available: getStyle() and setStyle() • $type: (Optional) The formatting type to use. If unset, it will use NumberFormatter::TYPE_DOUBLE as the default type. Methods for getting/setting the format type are also available: getType() and setType() NumberFormat Filter Usage 1 2 3 $filter = new ZendI18nFilterNumberFormat("de_DE"); echo $filter->filter(1234567.8912346); // Returns "1.234.567,891" 4 5 6 7 $filter = new ZendI18nFilterNumberFormat("en_US", NumberFormatter::PERCENT); echo $filter->filter(0.80); // Returns "80%" 8 9 10 11 $filter = new ZendI18nFilterNumberFormat("fr_FR", NumberFormatter::SCIENTIFIC); echo $filter->filter(0.00123456789); // Returns "1,23456789E-3" 108.14 PregReplace ZendFilterPregReplace performs a search using regular expressions and replaces all found elements. Supported Options The following options are supported for ZendFilterPregReplace: • pattern: The pattern which will be searched for. • replacement: The string which is used as replacement for the matches. Basic Usage To use this filter properly you must give two options: The option pattern has to be given to set the pattern which will be searched for. It can be a string for a single pattern, or an array of strings for multiple pattern. 108.14. PregReplace 475
  • 514. Zend Framework 2 Documentation, Release 2.2.5 To set the pattern which will be used as replacement the option replacement has to be used. It can be a string for a single pattern, or an array of strings for multiple pattern. 1 2 3 4 5 $filter = new ZendFilterPregReplace(array( ’pattern’ => ’/bob/’, ’replacement’ => ’john’, )); $input = ’Hi bob!’; 6 7 8 $filter->filter($input); // returns ’Hi john!’ You can use getPattern() and setPattern() to set the matching pattern afterwards. To set the replacement pattern you can use getReplacement() and setReplacement(). 1 2 3 4 $filter = new ZendFilterPregReplace(); $filter->setMatchPattern(array(’bob’, ’Hi’)) ->setReplacement(array(’john’, ’Bye’)); $input = ’Hi bob!’; 5 6 7 $filter->filter($input); // returns ’Bye john!’ For a more complex usage take a look into PHP‘s PCRE Pattern Chapter. 108.15 RealPath This filter will resolve given links and pathnames and returns canonicalized absolute pathnames. Supported Options The following options are supported for ZendFilterRealPath: • exists: This option defaults to TRUE which checks if the given path really exists. Basic Usage For any given link of pathname its absolute path will be returned. References to ‘/./‘, ‘/../‘ and extra ‘/‘ characters in the input path will be stripped. The resulting path will not have any symbolic link, ‘/./‘ or ‘/../‘ character. ZendFilterRealPath will return FALSE on failure, e.g. if the file does not exist. On BSD systems ZendFilterRealPath doesn’t fail if only the last path component doesn’t exist, while other systems will return FALSE. 1 2 3 $filter = new ZendFilterRealPath(); $path = ’/www/var/path/../../mypath’; $filtered = $filter->filter($path); 4 5 // returns ’/www/mypath’ Non-Existing Paths Sometimes it is useful to get also paths when they don’t exist, f.e. when you want to get the real path for a path which you want to create. You can then either give a FALSE at initiation, or use setExists() to set it. 476 Chapter 108. Standard Filter Classes
  • 515. Zend Framework 2 Documentation, Release 2.2.5 1 2 3 $filter = new ZendFilterRealPath(false); $path = ’/www/var/path/../../non/existing/path’; $filtered = $filter->filter($path); 4 5 6 // returns ’/www/non/existing/path’ // even when file_exists or realpath would return false 108.16 StringToLower This filter converts any input to be lowercased. Supported Options The following options are supported for ZendFilterStringToLower: • encoding: This option can be used to set an encoding which has to be used. Basic Usage This is a basic example: 1 $filter = new ZendFilterStringToLower(); 2 3 4 print $filter->filter(’SAMPLE’); // returns "sample" Different Encoded Strings Per default it will only handle characters from the actual locale of your server. Characters from other charsets would be ignored. Still, it’s possible to also lowercase them when the mbstring extension is available in your environment. Simply set the wished encoding when initiating the StringToLower filter. Or use the setEncoding() method to change the encoding afterwards. 1 2 // using UTF-8 $filter = new ZendFilterStringToLower(’UTF-8’); 3 4 5 // or give an array which can be useful when using a configuration $filter = new ZendFilterStringToLower(array(’encoding’ => ’UTF-8’)); 6 7 8 // or do this afterwards $filter->setEncoding(’ISO-8859-1’); Note: Setting wrong encodings Be aware that you will get an exception when you want to set an encoding and the mbstring extension is not available in your environment. Also when you are trying to set an encoding which is not supported by your mbstring extension you will get an exception. 108.16. StringToLower 477
  • 516. Zend Framework 2 Documentation, Release 2.2.5 108.17 StringToUpper This filter converts any input to be uppercased. Supported Options The following options are supported for ZendFilterStringToUpper: • encoding: This option can be used to set an encoding which has to be used. Basic Usage This is a basic example for using the StringToUpper filter: 1 $filter = new ZendFilterStringToUpper(); 2 3 4 print $filter->filter(’Sample’); // returns "SAMPLE" Different Encoded Strings Like the StringToLower filter, this filter handles only characters from the actual locale of your server. Using different character sets works the same as with StringToLower. 1 $filter = new ZendFilterStringToUpper(array(’encoding’ => ’UTF-8’)); 2 3 4 // or do this afterwards $filter->setEncoding(’ISO-8859-1’); 108.18 StringTrim This filter modifies a given string such that certain characters are removed from the beginning and end. Supported Options The following options are supported for ZendFilterStringTrim: • charlist: List of characters to remove from the beginning and end of the string. If this is not set or is null, the default behavior will be invoked, which is to remove only whitespace from the beginning and end of the string. Basic Usage A basic example of usage is below: 1 $filter = new ZendFilterStringTrim(); 2 3 print $filter->filter(’ This is (my) content: ’); The above example returns ‘This is (my) content:’. Notice that the whitespace characters have been removed. 478 Chapter 108. Standard Filter Classes
  • 517. Zend Framework 2 Documentation, Release 2.2.5 Default Behavior 1 2 $filter = new ZendFilterStringTrim(’:’); // or new ZendFilterStringTrim(array(’charlist’ => ’:’)); 3 4 print $filter->filter(’ This is (my) content:’); The above example returns ‘This is (my) content’. Notice that the whitespace characters and colon are removed. You can also provide a Traversable or an array with a ‘charlist’ key. To set the desired character list after instantiation, use the setCharList() method. The getCharList() return the values set for charlist. 108.19 StripNewLines This filter modifies a given string and removes all new line characters within that string. Supported Options There are no additional options for ZendFilterStripNewLines: Basic Usage A basic example of usage is below: 1 $filter = new ZendFilterStripNewLines(); 2 3 print $filter->filter(’ This is (my)‘‘nr‘‘content: ’); The above example returns ‘This is (my) content:’. Notice that all newline characters have been removed. 108.20 StripTags This filter can strip XML and HTML tags from given content. Warning: ZendFilterStripTags is potentially unsecure Be warned that ZendFilterStripTags should only be used to strip all available tags. Using ZendFilterStripTags to make your site secure by stripping some unwanted tags will lead to unsecure and dangerous code. ZendFilterStripTags must not be used to prevent XSS attacks. This filter is no replacement for using Tidy or HtmlPurifier. Supported Options The following options are supported for ZendFilterStripTags: • allowAttribs: This option sets the attributes which are accepted. All other attributes are stripped from the given content. • allowTags: This option sets the tags which are accepted. All other tags will be stripped 108.19. StripNewLines 479
  • 518. Zend Framework 2 Documentation, Release 2.2.5 from the given content. Basic Usage See the following example for the default behaviour of this filter: 1 $filter = new ZendFilterStripTags(); 2 3 print $filter->filter(’<B>My content</B>’); As result you will get the stripped content ‘My content’. When the content contains broken or partial tags then the complete following content will be erased. See the following example: 1 $filter = new ZendFilterStripTags(); 2 3 print $filter->filter(’This contains <a href="http://example.com">no ending tag’); The above will return ‘This contains’ with the rest being stripped. Allowing Defined Tags ZendFilterStripTags allows stripping of all but defined tags. This can be used for example to strip all tags but links from a text. 1 $filter = new ZendFilterStripTags(array(’allowTags’ => ’a’)); 2 3 4 $input = "A text with <br/> a <a href=’link.com’>link</a>"; print $filter->filter($input); The above will return ‘A text with a <a href=’link.com’>link</a>’ as result. It strips all tags but the link. By providing an array you can set multiple tags at once. Warning: Do not use this feature to get a probably secure content. This component does not replace the use of a proper configured html filter. Allowing Defined Attributes It is also possible to strip all but allowed attributes from a tag. 1 $filter = new ZendFilterStripTags(array(’allowTags’ => ’img’, ’allowAttribs’ => ’src’)); 2 3 4 $input = "A text with <br/> a <img src=’picture.com’ width=’100’>picture</img>"; print $filter->filter($input); The above will return ‘A text with a <img src=’picture.com’>picture</img>’ as result. It strips all tags but img. Additionally from the img tag all attributes but src will be stripped. By providing an array you can set multiple attributes at once. 480 Chapter 108. Standard Filter Classes
  • 519. Zend Framework 2 Documentation, Release 2.2.5 108.21 UriNormalize This filter can set a scheme on an URI, if a scheme is not present. If a scheme is present, that scheme will not be affected, even if a different scheme is enforced. Supported Options The following options are supported for ZendFilterUriNormalize: • defaultScheme: This option can be used to set the default scheme to use when parsing scheme-less URIs. • enforcedScheme: Set a URI scheme to enforce on schemeless URIs. Basic Usage See the following example for the default behaviour of this filter: 1 2 3 $filter = new ZendFilterUriNormalize(array( ’enforcedScheme’ => ’https’ )); 4 5 echo $filter->filter(’www.example.com’); As the result the string https://www.example.com will be output. 108.21. UriNormalize 481
  • 520. Zend Framework 2 Documentation, Release 2.2.5 482 Chapter 108. Standard Filter Classes
  • 521. CHAPTER 109 Word Filters In addition to the standard set of filters, there are several classes specific to filtering word strings. 109.1 CamelCaseToDash This filter modifies a given string such that ‘CamelCaseWords’ are converted to ‘Camel-Case-Words’. Supported Options There are no additional options for ZendFilterWordCamelCaseToDash: Basic Usage A basic example of usage is below: 1 $filter = new ZendFilterWordCamelCaseToDash(); 2 3 print $filter->filter(’ThisIsMyContent’); The above example returns ‘This-Is-My-Content’. 109.2 CamelCaseToSeparator This filter modifies a given string such that ‘CamelCaseWords’ are converted to ‘Camel Case Words’. Supported Options The following options are supported for ZendFilterWordCamelCaseToSeparator: • separator: A separator char. If this is not set the separator will be a space character. 483
  • 522. Zend Framework 2 Documentation, Release 2.2.5 Basic Usage A basic example of usage is below: 1 2 $filter = new ZendFilterWordCamelCaseToSeparator(’:’); // or new ZendFilterWordCamelCaseToSeparator(array(’separator’ => ’:’)); 3 4 print $filter->filter(’ThisIsMyContent’); The above example returns ‘This:Is:My:Content’. Default Behavior 1 $filter = new ZendFilterWordCamelCaseToSeparator(); 2 3 print $filter->filter(’ThisIsMyContent’); The above example returns ‘This Is My Content’. 109.3 CamelCaseToUnderscore This filter modifies a given string such that ‘CamelCaseWords’ are converted to ‘Camel_Case_Words’. Supported Options There are no additional options for ZendFilterWordCamelCaseToUnderscore: Basic usage A basic example of usage is below: 1 $filter = new ZendFilterWordCamelCaseToUnderscore(); 2 3 print $filter->filter(’ThisIsMyContent’); The above example returns ‘This_Is_My_Content’. 109.4 DashToCamelCase This filter modifies a given string such that ‘words-with-dashes’ are converted to ‘WordsWithDashes’. Supported Options There are no additional options for ZendFilterWordDashToCamelCase: 484 Chapter 109. Word Filters
  • 523. Zend Framework 2 Documentation, Release 2.2.5 Basic Usage A basic example of usage is below: 1 $filter = new ZendFilterWordDashToCamelCase(); 2 3 print $filter->filter(’this-is-my-content’); The above example returns ‘ThisIsMyContent’. 109.5 DashToSeparator This filter modifies a given string such that ‘words-with-dashes’ are converted to ‘words with dashes’. Supported Options The following options are supported for ZendFilterWordDashToSeparator: • separator: A separator char. If this is not set the separator will be a space character. Basic Usage A basic example of usage is below: 1 2 $filter = new ZendFilterWordDashToSeparator(’+’); // or new ZendFilterWordCamelCaseToSeparator(array(’separator’ => ’+’)); 3 4 print $filter->filter(’this-is-my-content’); The above example returns ‘this+is+my+content’. Default Behavior 1 $filter = new ZendFilterWordDashToSeparator(); 2 3 print $filter->filter(’this-is-my-content’); The above example returns ‘this is my content’. 109.6 DashToUnderscore This filter modifies a given string such that ‘words-with-dashes’ are converted to ‘words_with_dashes’. Supported Options There are no additional options for ZendFilterWordDashToUnderscore: 109.5. DashToSeparator 485
  • 524. Zend Framework 2 Documentation, Release 2.2.5 Basic Usage A basic example of usage is below: 1 $filter = new ZendFilterWordDashToUnderscore(); 2 3 print $filter->filter(’this-is-my-content’); The above example returns ‘this_is_my_content’. 109.7 SeparatorToCamelCase This filter modifies a given string such that ‘words with separators’ are converted to ‘WordsWithSeparators’. Supported Options The following options are supported for ZendFilterWordSeparatorToCamelCase: • separator: A separator char. If this is not set the separator will be a space character. Basic Usage A basic example of usage is below: 1 2 $filter = new ZendFilterWordSeparatorToCamelCase(’:’); // or new ZendFilterWordSeparatorToCamelCase(array(’separator’ => ’:’)); 3 4 print $filter->filter(’this:is:my:content’); The above example returns ‘ThisIsMyContent’. Default Behavior 1 $filter = new ZendFilterWordSeparatorToCamelCase(); 2 3 print $filter->filter(’this is my content’); The above example returns ‘ThisIsMyContent’. 109.8 SeparatorToDash This filter modifies a given string such that ‘words with separators’ are converted to ‘words-with-separators’. Supported Options The following options are supported for ZendFilterWordSeparatorToDash: • separator: A separator char. If this is not set the separator will be a space character. 486 Chapter 109. Word Filters
  • 525. Zend Framework 2 Documentation, Release 2.2.5 Basic Usage A basic example of usage is below: 1 2 $filter = new ZendFilterWordSeparatorToDash(’:’); // or new ZendFilterWordSeparatorToDash(array(’separator’ => ’:’)); 3 4 print $filter->filter(’this:is:my:content’); The above example returns ‘this-is-my-content’. Default Behavior 1 $filter = new ZendFilterWordSeparatorToDash(); 2 3 print $filter->filter(’this is my content’); The above example returns ‘this-is-my-content’. 109.9 SeparatorToSeparator This filter modifies a given string such that ‘words with separators’ are converted to ‘words-with-separators’. Supported Options The following options are supported for ZendFilterWordSeparatorToSeparator: • searchSeparator: The search separator char. If this is not set the separator will be a space character. • replaceSeparator: The replace separator char. If this is not set the separator will be a dash. Basic Usage A basic example of usage is below: 1 $filter = new ZendFilterWordSeparatorToSeparator(’:’, ’+’); 2 3 print $filter->filter(’this:is:my:content’); The above example returns ‘this+is+my+content’. Default Behaviour 1 $filter = new ZendFilterWordSeparatorToSeparator(); 2 3 print $filter->filter(’this is my content’); The above example returns ‘this-is-my-content’. 109.9. SeparatorToSeparator 487
  • 526. Zend Framework 2 Documentation, Release 2.2.5 109.10 UnderscoreToCamelCase This filter modifies a given string such that ‘words_with_underscores’ are converted to ‘WordsWithUnderscores’. Supported Options There are no additional options for ZendFilterWordUnderscoreToCamelCase: Basic Usage A basic example of usage is below: 1 $filter = new ZendFilterWordUnderscoreToCamelCase(); 2 3 print $filter->filter(’this_is_my_content’); The above example returns ‘ThisIsMyContent’. 109.11 UnderscoreToSeparator This filter modifies a given string such that ‘words_with_underscores’ are converted to ‘words with underscores’. Supported Options The following options are supported for ZendFilterWordUnderscoreToSeparator: • separator: A separator char. If this is not set the separator will be a space character. Basic usage A basic example of usage is below: 1 2 $filter = new ZendFilterWordUnderscoreToSeparator(’+’); // or new ZendFilterWordCamelCaseToSeparator(array(’separator’ => ’+’)); 3 4 print $filter->filter(’this_is_my_content’); The above example returns ‘this+is+my+content’. Default Behavior 1 $filter = new ZendFilterWordUnderscoreToSeparator(); 2 3 print $filter->filter(’this_is_my_content’); The above example returns ‘this is my content’. 488 Chapter 109. Word Filters
  • 527. Zend Framework 2 Documentation, Release 2.2.5 109.12 UnderscoreToDash This filter modifies a given string such that ‘words_with_underscores’ are converted to ‘words-with-underscores’. Supported Options There are no additional options for ZendFilterWordUnderscoreToDash: Basic usage A basic example of usage is below: 1 $filter = new ZendFilterWordUnderscoreToDash(); 2 3 print $filter->filter(’this_is_my_content’); The above example returns ‘this-is-my-content’. 109.12. UnderscoreToDash 489
  • 528. Zend Framework 2 Documentation, Release 2.2.5 490 Chapter 109. Word Filters
  • 529. CHAPTER 110 File Filter Classes Zend Framework comes with a set of classes for filtering file contents as well as performing other actions, such as file renaming. Note: All of the File Filter Classes’ filter() methods support both a file path string or a $_FILES array as the supplied argument. When a $_FILES array is passed in, the tmp_name is used for the file path. 110.1 Decrypt TODO 110.2 Encrypt TODO 110.3 Lowercase TODO 110.4 Rename ZendFilterFileRename can be used to rename a file and/or move a file to a new path. Supported Options The following set of options are supported: • target (string) default: "*" Target filename or directory, the new name of the source file. 491
  • 530. Zend Framework 2 Documentation, Release 2.2.5 • source (string) default: "*" Source filename or directory which will be renamed. Used to match the filtered file with an options set. • overwrite (boolean) default: false Shall existing files be overwritten? If the file is unable to be moved into the ZendFilterExceptionRuntimeException will be thrown. target path, a • randomize (boolean) default: false Shall target files have a random postfix attached? The random postfix will be a uniqid(’_’) after the file name and before the extension. For example, "file.txt" will be randomized to "file_4b3403665fea6.txt" An array of option sets is also supported, where a single Rename filter instance can filter several files using different options. The options used for the filtered file will be matched from the source option in the options set. Usage Examples Move all filtered files to a different directory: 1 2 3 4 // ’target’ option is assumed if param is a string $filter = ZendFilterFileRename("/tmp/"); echo $filter->filter("./myfile.txt"); // File has been moved to "/tmp/myfile.txt" Rename all filtered files to a new name: 1 2 3 $filter = ZendFilterFileRename("/tmp/newfile.txt"); echo $filter->filter("./myfile.txt"); // File has been renamed to "/tmp/newfile.txt" Move to a new path and randomize file names: 1 2 3 4 5 6 $filter = ZendFilterFileRename(array( "target" => "/tmp/newfile.txt", "randomize" => true, )); echo $filter->filter("./myfile.txt"); // File has been renamed to "/tmp/newfile_4b3403665fea6.txt" Configure different options for several possible source files: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $filter = ZendFilterFileRename(array( array( "source" => "fileA.txt" "target" => "/dest1/newfileA.txt", "overwrite" => true, ), array( "source" => "fileB.txt" "target" => "/dest2/newfileB.txt", "randomize" => true, ), )); echo $filter->filter("fileA.txt"); // File has been renamed to "/dest1/newfileA.txt" echo $filter->filter("fileB.txt"); // File has been renamed to "/dest2/newfileB_4b3403665fea6.txt" 492 Chapter 110. File Filter Classes
  • 531. Zend Framework 2 Documentation, Release 2.2.5 Public Methods The specific public methods for the Rename filter, besides the common filter() method, are as follows: getFile() Returns the files to rename and their new name and location Return type array setFile(string|array $options) Sets the file options for renaming. Removes any previously set file options. Parameters $options – See Supported Options section for more information. addFile(string|array $options) Adds file options for renaming to the current list of file options. Parameters $options – See Supported Options section for more information. 110.5 RenameUpload ZendFilterFileRenameUpload can be used to rename or move an uploaded file to a new path. Supported Options The following set of options are supported: • target (string) default: "*" Target directory or full filename path. • overwrite (boolean) default: false Shall existing files be overwritten? If the file is unable to be moved into the ZendFilterExceptionRuntimeException will be thrown. target path, a • randomize (boolean) default: false Shall target files have a random postfix attached? The random postfix will be a uniqid(’_’) after the file name and before the extension. For example, "file.txt" will be randomized to "file_4b3403665fea6.txt" • use_upload_name (boolean) default: false When true, this filter will use the $_FILES[’name’] as the target filename. Otherwise, the default target rules and the $_FILES[’tmp_name’] will be used. • use_upload_extension (boolean) default: original extension if not specified. false When true, the uploaded file will maintains its For example, if the uploaded file is "file.txt" and the target is something like "mynewfile", the upload will be renamed to "mynewfile.txt". Warning: Be very careful when using the use_upload_name option. For instance, extremely bad things could happen if you were to allow uploaded .php files (or other CGI files) to be moved into the DocumentRoot. It is generally a better idea to supply an internal filename to avoid security risks. RenameUpload does not support an array of options like the‘‘Rename‘‘ filter. When filtering HTML5 file uploads with the multiple attribute set, all files will be filtered with the same option settings. 110.5. RenameUpload 493
  • 532. Zend Framework 2 Documentation, Release 2.2.5 Usage Examples Move all filtered files to a different directory: 1 use ZendHttpPhpEnvironmentRequest; 2 3 4 5 6 $request = new Request(); $files = $request->getFiles(); // i.e. $files[’my-upload’][’tmp_name’] === ’/tmp/php5Wx0aJ’ // i.e. $files[’my-upload’][’name’] === ’myfile.txt’ 7 8 9 10 11 // ’target’ option is assumed if param is a string $filter = ZendFilterFileRenameUpload("./data/uploads/"); echo $filter->filter($files[’my-upload’]); // File has been moved to "./data/uploads/php5Wx0aJ" 12 13 14 15 16 // ... or retain the uploaded file name $filter->setUseUploadName(true); echo $filter->filter($files[’my-upload’]); // File has been moved to "./data/uploads/myfile.txt" Rename all filtered files to a new name: 1 use ZendHttpPhpEnvironmentRequest; 2 3 4 5 $request = new Request(); $files = $request->getFiles(); // i.e. $files[’my-upload’][’tmp_name’] === ’/tmp/php5Wx0aJ’ 6 7 8 9 $filter = ZendFilterFileRename("./data/uploads/newfile.txt"); echo $filter->filter($files[’my-upload’]); // File has been renamed to "./data/uploads/newfile.txt" Move to a new path and randomize file names: 1 use ZendHttpPhpEnvironmentRequest; 2 3 4 5 $request = new Request(); $files = $request->getFiles(); // i.e. $files[’my-upload’][’tmp_name’] === ’/tmp/php5Wx0aJ’ 6 7 8 9 10 11 12 $filter = ZendFilterFileRename(array( "target" => "./data/uploads/newfile.txt", "randomize" => true, )); echo $filter->filter($files[’my-upload’]); // File has been renamed to "./data/uploads/newfile_4b3403665fea6.txt" 110.6 Uppercase TODO 494 Chapter 110. File Filter Classes
  • 533. CHAPTER 111 Filter Chains Often multiple filters should be applied to some value in a particular order. For example, a login form accepts a username that should be only lowercase, alphabetic characters. ZendFilterFilterChain provides a simple method by which filters may be chained together. The following code illustrates how to chain together two filters for the submitted username: 1 2 3 4 // Create a filter chain and add filters to the chain $filterChain = new ZendFilterFilterChain(); $filterChain->attach(new ZendFilterAlpha()) ->attach(new ZendFilterStringToLower()); 5 6 7 // Filter the username $username = $filterChain->filter($_POST[’username’]); Filters are run in the order they were added to ZendFilterFilterChain. In the above example, the username is first removed of any non-alphabetic characters, and then any uppercase characters are converted to lowercase. Any object that implements ZendFilterFilterInterface may be used in a filter chain. 111.1 Setting Filter Chain Order For each filter added to the FilterChain you can set a priority to define the chain order. The default value is 1000. In the following example, any uppercase characters are converted to lowercase before any non-alphabetic characters are removed. 1 2 3 4 // Create a filter chain and add filters to the chain $filterChain = new ZendFilterFilterChain(); $filterChain->attach(new ZendFilterAlpha()) ->attach(new ZendFilterStringToLower(), 500); 111.2 Using the Plugin Manager To every FilterChain object an instance of the FilterPluginManager is attached. Every filter that is used in a FilterChain must be know by this FilterPluginManager. To add a filter that is known by the FilterPluginManager you can use the attachByName() method. The first parameter is the name of the 495
  • 534. Zend Framework 2 Documentation, Release 2.2.5 filter within the FilterPluginManager. The second parameter takes any options for creating the filter instance. The third parameter is the priority. 1 2 3 4 // Create a filter chain and add filters to the chain $filterChain = new ZendFilterFilterChain(); $filterChain->attachByName(’alpha’) ->attachByName(’stringtolower’, array(’encoding’ => ’utf-8’), 500); The following example shows how to add a custom filter to the FilterPluginManager and the FilterChain. 1 2 3 4 5 6 $filterChain = new ZendFilterFilterChain(); $filterChain->getPluginManager()->setInvokableClass( ’myNewFilter’, ’MyCustomFilterMyNewFilter’ ); $filterChain->attachByName(’alpha’) ->attachByName(’myNewFilter’); You can also add your own FilterPluginManager implementation. 1 2 3 4 $filterChain = new ZendFilterFilterChain(); $filterChain->setPluginManager(new MyFilterPluginManager()); $filterChain->attach(new ZendFilterAlpha()) ->attach(new MyCustomFilterMyNewFilter()); 496 Chapter 111. Filter Chains
  • 535. CHAPTER 112 ZendFilterInflector ZendFilterInflector is a general purpose tool for rules-based inflection of strings to a given target. As an example, you may find you need to transform MixedCase or camelCasedWords into a path; for readability, OS policies, or other reasons, you also need to lower case this, and you want to separate the words using a dash (‘-‘). An inflector can do this for you. ZendFilterInflector implements ZendFilterFilterInterface; you perform inflection by calling filter() on the object instance. Transforming MixedCase and camelCaseText to another format 1 2 3 4 5 $inflector = new ZendFilterInflector(’pages/:page.:suffix’); $inflector->setRules(array( ’:page’ => array(’WordCamelCaseToDash’, ’StringToLower’), ’suffix’ => ’html’, )); 6 7 8 9 $string = ’camelCasedWords’; $filtered = $inflector->filter(array(’page’ => $string)); // pages/camel-cased-words.html 10 11 12 13 $string = ’this_is_not_camel_cased’; $filtered = $inflector->filter(array(’page’ => $string)); // pages/this_is_not_camel_cased.html 112.1 Operation An inflector requires a target and one or more rules. A target is basically a string that defines placeholders for variables you wish to substitute. These are specified by prefixing with a ‘:’: :script. When calling filter(), you then pass in an array of key and value pairs corresponding to the variables in the target. Each variable in the target can have zero or more rules associated with them. Rules may be either static or refer to a ZendFilter class. Static rules will replace with the text provided. Otherwise, a class matching the rule provided will be used to inflect the text. Classes are typically specified using a short name indicating the filter name stripped of any common prefix. 497
  • 536. Zend Framework 2 Documentation, Release 2.2.5 As an example, you can use any ZendFilter concrete implementations; however, instead of referring to them as ‘ZendFilterAlpha‘ or ‘ZendFilterStringToLower‘, you’d specify only ‘Alpha‘ or ‘StringToLower‘. 112.2 Using Custom Filters ZendFilterInflector uses ZendFilterFilterPluginManager to manage loading filters to use with inflection. By default, filters registered with ZendFilterFilterPluginManager are available. To access filters with that prefix but which occur deeper in the hierarchy, such as the various Word filters, simply strip off the ZendFilter prefix: 1 2 // use ZendFilterWordCamelCaseToDash as a rule $inflector->addRules(array(’script’ => ’WordCamelCaseToDash’)); To use custom filters, you have two choices: reference them by fully qualified class name (e.g., MyCustomFilterMungify), or manipulate the composed FilterPluginManager instance. 1 2 $filters = $inflector->getPluginManager(); $filters->addInvokableClass(’mungify’, ’MyCustomFilterMungify’); 112.3 Setting the Inflector Target The inflector target is a string with some placeholders for variables. Placeholders take the form of an identifier, a colon (‘:’) by default, followed by a variable name: ‘:script’, ‘:path’, etc. The filter() method looks for the identifier followed by the variable name being replaced. You can change the identifier using the setTargetReplacementIdentifier() method, or passing it as the third argument to the constructor: 1 2 // Via constructor: $inflector = new ZendFilterInflector(’#foo/#bar.#sfx’, null, ’#’); 3 4 5 // Via accessor: $inflector->setTargetReplacementIdentifier(’#’); Typically, you will set the target via the constructor. However, you may want to re-set the target later. setTarget() can be used for this purpose: 1 $inflector->setTarget(’layouts/:script.phtml’); Additionally, you may wish to have a class member for your class that you can use to keep the inflector target updated – without needing to directly update the target each time (thus saving on method calls). setTargetReference() allows you to do this: 1 2 3 4 5 6 class Foo { /** * @var string Inflector target */ protected $_target = ’foo/:bar/:baz.:suffix’; 7 /** * Constructor * @return void */ 8 9 10 11 498 Chapter 112. ZendFilterInflector
  • 537. Zend Framework 2 Documentation, Release 2.2.5 public function __construct() { $this->_inflector = new ZendFilterInflector(); $this->_inflector->setTargetReference($this->_target); } 12 13 14 15 16 17 /** * Set target; updates target in inflector * * @param string $target * @return Foo */ public function setTarget($target) { $this->_target = $target; return $this; } 18 19 20 21 22 23 24 25 26 27 28 29 } 112.4 Inflection Rules As mentioned in the introduction, there are two types of rules: static and filter-based. Note: It is important to note that regardless of the method in which you add rules to the inflector, either one-byone, or all-at-once; the order is very important. More specific names, or names that might contain other rule names, must be added before least specific names. For example, assuming the two rule names ‘moduleDir’ and ‘module’, the ‘moduleDir’ rule should appear before module since ‘module’ is contained within ‘moduleDir’. If ‘module’ were added before ‘moduleDir’, ‘module’ will match part of ‘moduleDir’ and process it leaving ‘Dir’ inside of the target uninflected. 112.4.1 Static Rules Static rules do simple string substitution; use them when you have a segment in the target that will typically be static, but which you want to allow the developer to modify. Use the setStaticRule() method to set or modify the rule: 1 2 $inflector = new ZendFilterInflector(’:script.:suffix’); $inflector->setStaticRule(’suffix’, ’phtml’); 3 4 5 // change it later: $inflector->setStaticRule(’suffix’, ’php’); Much like the target itself, you can also bind a static rule to a reference, allowing you to update a single variable instead of require a method call; this is often useful when your class uses an inflector internally, and you don’t want your users to need to fetch the inflector in order to update it. The setStaticRuleReference() method is used to accomplish this: 1 2 3 4 5 6 class Foo { /** * @var string Suffix */ protected $_suffix = ’phtml’; 7 112.4. Inflection Rules 499
  • 538. Zend Framework 2 Documentation, Release 2.2.5 /** * Constructor * @return void */ public function __construct() { $this->_inflector = new ZendFilterInflector(’:script.:suffix’); $this->_inflector->setStaticRuleReference(’suffix’, $this->_suffix); } 8 9 10 11 12 13 14 15 16 17 /** * Set suffix; updates suffix static rule in inflector * * @param string $suffix * @return Foo */ public function setSuffix($suffix) { $this->_suffix = $suffix; return $this; } 18 19 20 21 22 23 24 25 26 27 28 29 } 112.4.2 Filter Inflector Rules ZendFilter filters may be used as inflector rules as well. Just like static rules, these are bound to a target variable; unlike static rules, you may define multiple filters to use when inflecting. These filters are processed in order, so be careful to register them in an order that makes sense for the data you receive. Rules may be added using setFilterRule() (which overwrites any previous rules for that variable) or addFilterRule() (which appends the new rule to any existing rule for that variable). Filters are specified in one of the following ways: • String. The string may be a filter class name, or a class name segment minus any prefix set in the inflector’s plugin loader (by default, minus the ‘ZendFilter‘ prefix). • Filter object. Any object instance implementing ZendFilterFilterInterface may be passed as a filter. • Array. An array of one or more strings or filter objects as defined above. 1 $inflector = new ZendFilterInflector(’:script.:suffix’); 2 3 4 // Set rule to use ZendFilterWordCamelCaseToDash filter $inflector->setFilterRule(’script’, ’WordCamelCaseToDash’); 5 6 7 // Add rule to lowercase string $inflector->addFilterRule(’script’, new ZendFilterStringToLower()); 8 9 10 11 12 13 // Set rules en-masse $inflector->setFilterRule(’script’, array( ’WordCamelCaseToDash’, new ZendFilterStringToLower() )); 500 Chapter 112. ZendFilterInflector
  • 539. Zend Framework 2 Documentation, Release 2.2.5 112.4.3 Setting Many Rules At Once Typically, it’s easier to set many rules at once than to configure a single variable and its inflection rules at a time. ZendFilterInflector‘s addRules() and setRules() method allow this. Each method takes an array of variable and rule pairs, where the rule may be whatever the type of rule accepts (string, filter object, or array). Variable names accept a special notation to allow setting static rules and filter rules, according to the following notation: • ‘:’ prefix: filter rules. • No prefix: static rule. Setting Multiple Rules at Once 1 2 3 4 5 // Could also use setRules() with this notation: $inflector->addRules(array( // filter rules: ’:controller’ => array(’CamelCaseToUnderscore’,’StringToLower’), ’:action’ => array(’CamelCaseToUnderscore’,’StringToLower’), 6 // Static rule: ’suffix’ => ’phtml’ 7 8 9 )); 112.5 Utility Methods ZendFilterInflector has a number of utility methods for retrieving and setting the plugin loader, manipulating and retrieving rules, and controlling if and when exceptions are thrown. • setPluginManager() can be used when you have configured your own ZendFilterFilterPluginManager instance and wish to use it with ZendFilterInflector; getPluginManager() retrieves the currently set one. • setThrowTargetExceptionsOn() can be used to control whether or not filter() throws an exception when a given replacement identifier passed to it is not found in the target. By default, no exceptions are thrown. isThrowTargetExceptionsOn() will tell you what the current value is. • getRules($spec = null) can be used to retrieve all registered rules for all variables, or just the rules for a single variable. • getRule($spec, $index) fetches a single rule for a given variable; this can be useful for fetching a specific filter rule for a variable that has a filter chain. $index must be passed. • clearRules() will clear all currently registered rules. 112.6 Using a Traversable or an array with ZendFilterInflector You can use a Traversable or an array to set rules and other object state in your inflectors, either by passing a Traversable object or an array to the constructor or setOptions(). The following settings may be specified: • target specifies the inflection target. 112.5. Utility Methods 501
  • 540. Zend Framework 2 Documentation, Release 2.2.5 • pluginManager specifies the ZendFilterFilterPluginManager instance or extension to use for obtaining plugins; alternately, you may specify a class name of a class that extends the FilterPluginManager. • throwTargetExceptionsOn should be a boolean indicating whether or not to throw exceptions when a replacement identifier is still present after inflection. • targetReplacementIdentifier specifies the character to use when identifying replacement variables in the target string. • rules specifies an array of inflection rules; it should consist of keys that specify either values or arrays of values, consistent with addRules(). Using a Traversable or an array with ZendFilterInflector 1 2 3 // With the constructor: $options; // implements Traversable $inflector = new ZendFilterInflector($options); 4 5 6 7 // Or with setOptions(): $inflector = new ZendFilterInflector(); $inflector->setOptions($options); 502 Chapter 112. ZendFilterInflector
  • 541. CHAPTER 113 Writing Filters ZendFilter supplies a set of commonly needed filters, but developers will often need to write custom filters for their particular use cases. The task of writing a custom filter is facilitated by implementing ZendFilterFilterInterface. ZendFilterFilterInterface defines a single method, filter(), that may be implemented by user classes. The following example demonstrates how to write a custom filter: 1 namespace ApplicationFilter; 2 3 use ZendFilterFilterInterface; 4 5 6 7 8 9 class MyFilter implements FilterInterface { public function filter($value) { // perform some transformation upon $value to arrive on $valueFiltered 10 return $valueFiltered; 11 } 12 13 } To attach an instance of the filter defined above to a filter chain: 1 2 $filterChain = new ZendFilterFilterChain(); $filterChain->attach(new ApplicationFilterMyFilter()); 503
  • 542. Zend Framework 2 Documentation, Release 2.2.5 504 Chapter 113. Writing Filters
  • 543. CHAPTER 114 Introduction to ZendForm ZendForm is intended primarily as a bridge between your domain models and the View Layer. It composes a thin layer of objects representing form elements, an InputFilter, and a small number of methods for binding data to and from the form and attached objects. The ZendForm component consists of the following objects: • Elements, which simply consist of a name and attributes. • Fieldsets, which extend from Elements, but allow composing other fieldsets and elements. • Forms, which extend from Fieldsets (and thus Elements). They provide data and object binding, and compose InputFilters. Data binding is done via ZendStdlibHydrator. To facilitate usage with the view layer, the ZendForm component also aggregates a number of form-specific view helpers. These accept elements, fieldsets, and/or forms, and use the attributes they compose to render markup. A small number of specialized elements are provided for accomplishing application-centric tasks. These include the Csrf element, used to prevent Cross Site Request Forgery attacks, and the Captcha element, used to display and validate CAPTCHAs. A Factory is provided to facilitate creation of elements, fieldsets, forms, and the related input filter. The default Form implementation is backed by a factory to facilitate extension and ease the process of form creation. The code related to forms can often spread between a variety of concerns: a form definition, an input filter definition, a domain model class, and one or more hydrator implementations. As such, finding the various bits of code and how they relate can become tedious. To simplify the situation, you can also annotate your domain model class, detailing the various input filter definitions, attributes, and hydrators that should all be used together. ZendFormAnnotationAnnotationBuilder can then be used to build the various objects you need. 505
  • 544. Zend Framework 2 Documentation, Release 2.2.5 506 Chapter 114. Introduction to ZendForm
  • 545. CHAPTER 115 Form Quick Start Forms are relatively easy to create. At the bare minimum, each element or fieldset requires a name; typically, you’ll also provide some attributes to hint to the view layer how it might render the item. The form itself will also typically compose an InputFilter– which you can also conveniently create directly in the form via a factory. Individual elements can hint as to what defaults to use when generating a related input for the input filter. Form validation is as easy as providing an array of data to the setData() method. If you want to simplify your work even more, you can bind an object to the form; on successful validation, it will be populated from the validated values. 115.1 Programmatic Form Creation If nothing else, you can simply start creating elements, fieldsets, and forms and wiring them together. 1 2 3 4 5 6 use use use use use use ZendCaptcha; ZendFormElement; ZendFormFieldset; ZendFormForm; ZendInputFilterInput; ZendInputFilterInputFilter; 7 8 9 10 11 12 $name = new Element(’name’); $name->setLabel(’Your name’); $name->setAttributes(array( ’type’ => ’text’ )); 13 14 15 $email = new ElementEmail(’email’); $email->setLabel(’Your email address’); 16 17 18 19 20 21 $subject = new Element(’subject’); $subject->setLabel(’Subject’); $subject->setAttributes(array( ’type’ => ’text’ )); 22 23 24 $message = new ElementTextarea(’message’); $message->setLabel(’Message’); 507
  • 546. Zend Framework 2 Documentation, Release 2.2.5 25 26 27 28 $captcha = new ElementCaptcha(’captcha’); $captcha->setCaptcha(new CaptchaDumb()); $captcha->setLabel(’Please verify you are human’); 29 30 $csrf = new ElementCsrf(’security’); 31 32 33 34 35 36 $send = new Element(’send’); $send->setValue(’Submit’); $send->setAttributes(array( ’type’ => ’submit’ )); 37 38 39 40 41 42 43 44 45 46 $form = new Form(’contact’); $form->add($name); $form->add($email); $form->add($subject); $form->add($message); $form->add($captcha); $form->add($csrf); $form->add($send); 47 48 49 50 51 $nameInput = new Input(’name’); // configure input... and all others $inputFilter = new InputFilter(); // attach all inputs 52 53 $form->setInputFilter($inputFilter); As a demonstration of fieldsets, let’s alter the above slightly. We’ll create two fieldsets, one for the sender information, and another for the message details. 1 2 3 $sender = new Fieldset(’sender’); $sender->add($name); $sender->add($email); 4 5 6 7 $details = new Fieldset(’details’); $details->add($subject); $details->add($message); 8 9 10 11 12 13 14 $form = new Form(’contact’); $form->add($sender); $form->add($details); $form->add($captcha); $form->add($csrf); $form->add($send); Regardless of approach, as you can see, this can be tedious. 115.2 Creation via Factory You can create the entire form, and input filter, using the Factory. This is particularly nice if you want to store your forms as pure configuration; you can simply pass the configuration to the factory and be done. 508 Chapter 115. Form Quick Start
  • 547. Zend Framework 2 Documentation, Release 2.2.5 1 use ZendFormFactory; 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 47 48 49 50 51 52 53 54 55 56 57 58 $factory = new Factory(); $form = $factory->createForm(array( ’hydrator’ => ’ZendStdlibHydratorArraySerializable’, ’elements’ => array( array( ’spec’ => array( ’name’ => ’name’, ’options’ => array( ’label’ => ’Your name’, ), ’type’ => ’Text’, ) ), array( ’spec’ => array( ’type’ => ’ZendFormElementEmail’, ’name’ => ’email’, ’options’ => array( ’label’ => ’Your email address’, ) ), ), array( ’spec’ => array( ’name’ => ’subject’, ’options’ => array( ’label’ => ’Subject’, ), ’type’ => ’Text’, ), ), array( ’spec’ => array( ’type’ => ’ZendFormElementTextarea’, ’name’ => ’message’, ’options’ => array( ’label’ => ’Message’, ) ), ), array( ’spec’ => array( ’type’ => ’ZendFormElementCaptcha’, ’name’ => ’captcha’, ’options’ => array( ’label’ => ’Please verify you are human.’, ’captcha’ => array( ’class’ => ’Dumb’, ), ), ), ), array( ’spec’ => array( ’type’ => ’ZendFormElementCsrf’, ’name’ => ’security’, 115.2. Creation via Factory 509
  • 548. Zend Framework 2 Documentation, Release 2.2.5 ), ), array( ’spec’ => array( ’name’ => ’send’, ’type’ => ’Submit’, ’attributes’ => array( ’value’ => ’Submit’, ), ), ), 59 60 61 62 63 64 65 66 67 68 69 ), /* If we had fieldsets, they’d go here; fieldsets contain * "elements" and "fieldsets" keys, and potentially a "type" * key indicating the specific FieldsetInterface * implementation to use. ’fieldsets’ => array( ), */ 70 71 72 73 74 75 76 77 78 // Configuration to pass on to // ZendInputFilterFactory::createInputFilter() ’input_filter’ => array( /* ... */ ), 79 80 81 82 83 84 )); If we wanted to use fieldsets, as we demonstrated in the previous example, we could do the following: 1 use ZendFormFactory; 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 $factory = new Factory(); $form = $factory->createForm(array( ’hydrator’ => ’ZendStdlibHydratorArraySerializable’, ’fieldsets’ => array( array( ’spec’ => array( ’name’ => ’sender’, ’elements’ => array( array( ’spec’ => array( ’name’ => ’name’, ’options’ => array( ’label’ => ’Your name’, ), ’type’ => ’Text’ ), ), array( ’spec’ => array( ’type’ => ’ZendFormElementEmail’, ’name’ => ’email’, ’options’ => array( ’label’ => ’Your email address’, ), ), ), ), ), 510 Chapter 115. Form Quick Start
  • 549. Zend Framework 2 Documentation, Release 2.2.5 31 32 33 34 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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 ), array( ’spec’ => array( ’name’ => ’details’, ’elements’ => array( array( ’spec’ => array( ’name’ => ’subject’, ’options’ => array( ’label’ => ’Subject’, ), ’type’ => ’Text’, ), ), array( ’spec’ => array( ’name’ => ’message’, ’type’ => ’ZendFormElementTextarea’, ’options’ => array( ’label’ => ’Message’, ), ), ), ), ), ), ), ’elements’ => array( array( ’spec’ => array( ’type’ => ’ZendFormElementCaptcha’, ’name’ => ’captcha’, ’options’ => array( ’label’ => ’Please verify you are human. ’, ’captcha’ => array( ’class’ => ’Dumb’, ), ), ), ), array( ’spec’ => array( ’type’ => ’ZendFormElementCsrf’, ’name’ => ’security’, ), ), array( ’spec’ => array( ’name’ => ’send’, ’type’ => ’Submit’, ’attributes’ => array( ’value’ => ’Submit’, ), ), ), ), // Configuration to pass on to // ZendInputFilterFactory::createInputFilter() 115.2. Creation via Factory 511
  • 550. Zend Framework 2 Documentation, Release 2.2.5 ’input_filter’ => array( /* ... */ ), 89 90 91 92 )); Note that the chief difference is nesting; otherwise, the information is basically the same. The chief benefits to using the Factory are allowing you to store definitions in configuration, and usage of significant whitespace. 115.3 Factory-backed Form Extension The default Form implementation is backed by the Factory. This allows you to extend it, and define your form internally. This has the benefit of allowing a mixture of programmatic and factory-backed creation, as well as defining a form for re-use in your application. 1 namespace Contact; 2 3 4 5 use ZendCaptchaAdapterInterface as CaptchaAdapter; use ZendFormElement; use ZendFormForm; 6 7 8 9 class ContactForm extends Form { protected $captcha; 10 public function __construct(CaptchaAdapter $captcha) { $this->captcha = $captcha; 11 12 13 14 // add() can take either an Element/Fieldset instance, // or a specification, from which the appropriate object // will be built. 15 16 17 18 $this->add(array( ’name’ => ’name’, ’options’ => array( ’label’ => ’Your name’, ), ’type’ => ’Text’, )); $this->add(array( ’type’ => ’ZendFormElementEmail’, ’name’ => ’email’, ’options’ => array( ’label’ => ’Your email address’, ), )); $this->add(array( ’name’ => ’subject’, ’options’ => array( ’label’ => ’Subject’, ), ’type’ => ’Text’, )); $this->add(array( 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 512 Chapter 115. Form Quick Start
  • 551. Zend Framework 2 Documentation, Release 2.2.5 ’type’ => ’ZendFormElementTextarea’, ’name’ => ’message’, ’options’ => array( ’label’ => ’Message’, ), 41 42 43 44 45 )); $this->add(array( ’type’ => ’ZendFormElementCaptcha’, ’name’ => ’captcha’, ’options’ => array( ’label’ => ’Please verify you are human.’, ’captcha’ => $this->captcha, ), )); $this->add(new ElementCsrf(’security’)); $this->add(array( ’name’ => ’send’, ’type’ => ’Submit’, ’attributes’ => array( ’value’ => ’Submit’, ), )); 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 // We could also define the input filter here, or // lazy-create it in the getInputFilter() method. 64 65 } 66 67 } You’ll note that this example, the elements are added in the constructor. This is done to allow altering and/or configuring either the form or input filter factory instances, which could then have bearing on how elements, inputs, etc. are created. In this case, it also allows injection of the CAPTCHA adapter, allowing us to configure it elsewhere in our application and inject it into the form. 115.4 Validating Forms Validating forms requires three steps. First, the form must have an input filter attached. Second, you must inject the data to validate into the form. Third, you validate the form. If invalid, you can retrieve the error messages, if any. 1 $form = new ContactContactForm(); 2 3 4 // If the form doesn’t define an input filter by default, inject one. $form->setInputFilter(new ContactContactFilter()); 5 6 7 8 // Get the data. In an MVC application, you might try: $data = $request->getPost(); // for POST data $data = $request->getQuery(); // for GET (or query string) data 9 10 $form->setData($data); 11 12 13 14 15 16 17 // Validate the form if ($form->isValid()) { $validatedData = $form->getData(); } else { $messages = $form->getMessages(); } 115.4. Validating Forms 513
  • 552. Zend Framework 2 Documentation, Release 2.2.5 You can get the raw data if you want, by accessing the composed input filter. 1 $filter = $form->getInputFilter(); 2 3 4 $rawValues = $filter->getRawValues(); $nameRawValue = $filter->getRawValue(’name’); 115.5 Hinting to the Input Filter Often, you’ll create elements that you expect to behave in the same way on each usage, and for which you’ll want specific filters or validation as well. Since the input filter is a separate object, how can you achieve these latter points? Because the default form implementation composes a factory, and the default factory composes an input filter factory, you can have your elements and/or fieldsets hint to the input filter. If no input or input filter is provided in the input filter for that element, these hints will be retrieved and used to create them. To do so, one of the following must occur. For elements, they must implement ZendInputFilterInputProviderInterface, which defines a getInputSpecification() method; for fieldsets, they must implement ZendInputFilterInputFilterProviderInterface, which defines a getInputFilterSpecification() method. In the case of an element, the getInputSpecification() method should return data to be used by the input filter factory to create an input. Every HTML5 (email, url, color) elements have a built-in element that use this logic. For instance, here is how the ZendFormElementColor element is defined: 1 namespace ZendFormElement; 2 3 4 5 6 use use use use ZendFormElement; ZendInputFilterInputProviderInterface; ZendValidatorRegex as RegexValidator; ZendValidatorValidatorInterface; 7 8 9 10 11 12 13 14 15 16 17 class Color extends Element implements InputProviderInterface { /** * Seed attributes * * @var array */ protected $attributes = array( ’type’ => ’color’, ); 18 /** * @var ValidatorInterface */ protected $validator; 19 20 21 22 23 /** * Get validator * * @return ValidatorInterface */ protected function getValidator() { if (null === $this->validator) { $this->validator = new RegexValidator(’/^#[0-9a-fA-F]{6}$/’); 24 25 26 27 28 29 30 31 32 514 Chapter 115. Form Quick Start
  • 553. Zend Framework 2 Documentation, Release 2.2.5 } return $this->validator; 33 34 } 35 36 /** * Provide default input rules for this element * * Attaches an email validator. * * @return array */ public function getInputSpecification() { return array( ’name’ => $this->getName(), ’required’ => true, ’filters’ => array( array(’name’ => ’ZendFilterStringTrim’), array(’name’ => ’ZendFilterStringToLower’), ), ’validators’ => array( $this->getValidator(), ), ); } 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 } The above would hint to the input filter to create and attach an input named after the element, marking it as required, and giving it a StringTrim and StringToLower filters and a Regex validator. Note that you can either rely on the input filter to create filters and validators, or directly instantiate them. For fieldsets, you do very similarly; the difference is that getInputFilterSpecification() must return configuration for an input filter. 1 namespace ContactForm; 2 3 4 5 use ZendFormFieldset; use ZendInputFilterInputFilterProviderInterface; use ZendValidator; 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class SenderFieldset extends Fieldset implements InputFilterProviderInterface { public function getInputFilterSpecification() { return array( ’name’ => array( ’required’ => true, ’filters’ => array( array(’name’ => ’ZendFilterStringTrim’), ), ), ’email’ => array( ’required’ => true, ’filters’ => array( array(’name’ => ’ZendFilterStringTrim’), ), ’validators’ => array( new ValidatorEmailAddress(), ), 115.5. Hinting to the Input Filter 515
  • 554. Zend Framework 2 Documentation, Release 2.2.5 ), 26 ); 27 } 28 29 } Specifications are a great way to make forms, fieldsets, and elements re-usable trivially in your applications. In fact, the Captcha and Csrf elements define specifications in order to ensure they can work without additional user configuration! Note: If you set custom input filter specification either in getInputSpecification() or in getInputFilterSpecification(), the ZendInputFilterInputInterface set for that specific field is reset to the default ZendInputFilterInput. Some form elements may need a particular input filter, like ZendFormElementFile: in this case it’s mandatory to specify the type key in your custom specification to match the original one (in ex. for the file element it’s ZendInputFilterFileInput). 115.6 Binding an object As noted in the intro, forms in Zend Framework bridge the domain model and the view layer. Let’s see that in action. When you bind() an object to the form, the following happens: • The composed Hydrator calls extract() on the object, and uses the values returned, if any, to populate the value attributes of all elements. If a form contains a fieldset that itself contains another fieldset, the form will recursively extract the values. • When isValid() is called, if setData() has not been previously set, the form uses the composed Hydrator to extract values from the object, and uses those during validation. • If isValid() is successful (and the bindOnValidate flag is enabled, which is true by default), then the Hydrator will be passed the validated values to use to hydrate the bound object. (If you do not want this behavior, call setBindOnValidate(FormInterface::BIND_MANUAL)). • If the object implements ZendInputFilterInputFilterAwareInterface, the input filter it composes will be used instead of the one composed on the form. This is easier to understand in practice. 1 2 3 $contact = new ArrayObject; $contact[’subject’] = ’[Contact Form] ’; $contact[’message’] = ’Type your message here’; 4 5 $form = new ContactContactForm; 6 7 8 $form->bind($contact); // form now has default values for // ’subject’ and ’message’ 9 10 11 12 13 14 15 $data = array( ’name’ => ’John Doe’, ’email’ => ’[email protected]’, ’subject’ => ’[Contact Form] ’sup?’, ); $form->setData($data); 16 17 if ($form->isValid()) { 516 Chapter 115. Form Quick Start
  • 555. Zend Framework 2 Documentation, Release 2.2.5 // // // // // // // // 18 19 20 21 22 23 24 25 26 $contact now looks like: array( ’name’ => ’John Doe’, ’email’ => ’[email protected]’, ’subject’ => ’[Contact Form] ’sup?’, ’message’ => ’Type your message here’, ) only as an ArrayObject } When an object is bound to the form, calling getData() will return that object by default. If you want to return an associative array instead, you can pass the FormInterface::VALUES_AS_ARRAY flag to the method. 1 2 use ZendFormFormInterface; $data = $form->getData(FormInterface::VALUES_AS_ARRAY); Zend Framework ships several standard hydrators, and implementation is as simple as implementing ZendStdlibHydratorHydratorInterface, which looks like this: 1 namespace ZendStdlibHydrator; 2 3 4 5 6 7 8 interface HydratorInterface { /** @return array */ public function extract($object); public function hydrate(array $data, $object); } 115.7 Rendering As noted previously, forms are meant to bridge the domain model and view layer. We’ve discussed the domain model binding, but what about the view? The form component ships a set of form-specific view helpers. These accept the various form objects, and introspect them in order to generate markup. Typically, they will inspect the attributes, but in special cases, they may look at other properties and composed objects. When preparing to render, you will likely want to call prepare(). This method ensures that certain injections are done, and will likely in the future munge names to allow for scoped[array][notation]. The simplest view helpers available are Form, FormElement, FormLabel, and FormElementErrors. Let’s use them to display the contact form. 1 2 3 4 <?php // within a view script $form = $this->form; $form->prepare(); 5 6 7 // Assuming the "contact/process" route exists... $form->setAttribute(’action’, $this->url(’contact/process’)); 8 9 10 // Set the method attribute for the form $form->setAttribute(’method’, ’post’); 11 12 13 // Get the form label plugin $formLabel = $this->plugin(’formLabel’); 14 115.7. Rendering 517
  • 556. Zend Framework 2 Documentation, Release 2.2.5 15 16 17 18 19 20 21 22 23 24 25 // Render the opening tag echo $this->form()->openTag($form); ?> <div class="form_element"> <?php $name = $form->get(’name’); echo $formLabel->openTag() . $name->getOption(’label’); echo $this->formInput($name); echo $this->formElementErrors($name); echo $formLabel->closeTag(); ?></div> 26 27 28 29 30 31 32 33 34 <div class="form_element"> <?php $subject = $form->get(’subject’); echo $formLabel->openTag() . $subject->getOption(’label’); echo $this->formInput($subject); echo $this->formElementErrors($subject); echo $formLabel->closeTag(); ?></div> 35 36 37 38 39 40 41 42 43 <div class="form_element"> <?php $message = $form->get(’message’); echo $formLabel->openTag() . $message->getOption(’label’); echo $this->formTextarea($message); echo $this->formElementErrors($message); echo $formLabel->closeTag(); ?></div> 44 45 46 47 48 49 50 51 52 <div class="form_element"> <?php $captcha = $form->get(’captcha’); echo $formLabel->openTag() . $captcha->getOption(’label’); echo $this->formCaptcha($captcha); echo $this->formElementErrors($captcha); echo $formLabel->closeTag(); ?></div> 53 54 55 <?php echo $this->formElement($form->get(’security’)) ?> <?php echo $this->formElement($form->get(’send’)) ?> 56 57 <?php echo $this->form()->closeTag() ?> There are a few things to note about this. First, to prevent confusion in IDEs and editors when syntax highlighting, we use helpers to both open and close the form and label tags. Second, there’s a lot of repetition happening here; we could easily create a partial view script or a composite helper to reduce boilerplate. Third, note that not all elements are created equal – the CSRF and submit elements don’t need labels or error messages necessarily. Finally, note that the FormElement helper tries to do the right thing – it delegates actual markup generation to other view helpers; however, it can only guess what specific form helper to delegate to based on the list it has. If you introduce new form view helpers, you’ll need to extend the FormElement helper, or create your own. However, your view files can quickly become long and repetitive to write. While we do not currently provide a singleline form view helper (as this reduces the form customization), the simplest and most recommended way to render your form is by using the FormRow view helper. This view helper automatically renders a label (if present), the element itself using the FormElement helper, as well as any errors that could arise. Here is the previous form, rewritten to take advantage of this helper : 518 Chapter 115. Form Quick Start
  • 557. Zend Framework 2 Documentation, Release 2.2.5 1 2 3 4 <?php // within a view script $form = $this->form; $form->prepare(); 5 6 7 // Assuming the "contact/process" route exists... $form->setAttribute(’action’, $this->url(’contact/process’)); 8 9 10 // Set the method attribute for the form $form->setAttribute(’method’, ’post’); 11 12 13 14 15 16 17 18 19 // Render the opening tag echo $this->form()->openTag($form); ?> <div class="form_element"> <?php $name = $form->get(’name’); echo $this->formRow($name); ?></div> 20 21 22 23 24 25 <div class="form_element"> <?php $subject = $form->get(’subject’); echo $this->formRow($subject); ?></div> 26 27 28 29 30 31 <div class="form_element"> <?php $message = $form->get(’message’); echo $this->formRow($message); ?></div> 32 33 34 35 36 37 <div class="form_element"> <?php $captcha = $form->get(’captcha’); echo $this->formRow($captcha); ?></div> 38 39 40 <?php echo $this->formElement($form->get(’security’)) ?> <?php echo $this->formElement($form->get(’send’)) ?> 41 42 <?php echo $this->form()->closeTag() ?> Note that FormRow helper automatically prepends the label. If you want it to be rendered after the element itself, you can pass an optional parameter to the FormRow view helper : 1 2 3 4 5 <div class="form_element"> <?php $name = $form->get(’name’); echo $this->formRow($name, **’append’**); ?></div> 115.7.1 Taking advantage of HTML5 input attributes HTML5 brings a lot of exciting features, one of them being a simplified client form validations. Adding HTML5 attributes is simple as you just need to add specify the attributes. However, please note that adding those attributes does not automatically add Zend validators to the form’s input filter. You still need to manually add them. 115.7. Rendering 519
  • 558. Zend Framework 2 Documentation, Release 2.2.5 1 2 3 4 5 6 7 8 9 10 11 $form->add(array( ’name’ => ’phoneNumber’, ’options’ => array( ’label’ => ’Your phone number’ ), ’attributes’ => array( ’type’ => ’tel’ ’required’ => ’required’, ’pattern’ => ’^0[1-68]([-. ]?[0-9]{2}){4}$’ ) )); View helpers will automatically render those attributes, and hence allowing modern browsers to perform automatic validation. Note: Although client validation is nice from a user experience point of view, it has to be used in addition with server validation, as client validation can be easily fooled. 115.8 Validation Groups Sometimes you want to validate only a subset of form elements. As an example, let’s say we’re re-using our contact form over a web service; in this case, the Csrf, Captcha, and submit button elements are not of interest, and shouldn’t be validated. ZendForm provides a proxy method to the underlying InputFilter‘s setValidationGroup() method, allowing us to perform this operation. 1 2 3 4 5 6 $form->setValidationGroup(’name’, ’email’, ’subject’, ’message’); $form->setData($data); if ($form->isValid()) { // Contains only the "name", "email", "subject", and "message" values $data = $form->getData(); } If you later want to reset the form to validate all, simply pass the FormInterface::VALIDATE_ALL flag to the setValidationGroup() method. 1 2 use ZendFormFormInterface; $form->setValidationGroup(FormInterface::VALIDATE_ALL); When your form contains nested fieldsets, you can use an array notation to validate only a subset of the fieldsets : 1 2 3 4 5 6 7 8 9 10 11 12 $form->setValidationGroup(array( ’profile’ => array( ’firstname’, ’lastname’ ) )); $form->setData($data); if ($form->isValid()) { // Contains only the "firstname" and "lastname" values from the // "profile" fieldset $data = $form->getData(); } 520 Chapter 115. Form Quick Start
  • 559. Zend Framework 2 Documentation, Release 2.2.5 115.9 Using Annotations Creating a complete forms solution can often be tedious: you’ll create some domain model object, an input filter for validating it, a form object for providing a representation for it, and potentially a hydrator for mapping the form elements and fieldsets to the domain model. Wouldn’t it be nice to have a central place to define all of these? Annotations allow us to solve this problem. You can define the following behaviors with the shipped annotations in ZendForm: • AllowEmpty: mark an input as allowing an empty value. This annotation does not require a value. • Attributes: specify the form, fieldset, or element attributes. This annotation requires an associative array of values, in a JSON object format: @Attributes({"class":"zend_form","type":"text"}). • ComposedObject: specify another object with annotations to parse. Typically, this is used if a property references another object, which will then be added to your form as an additional fieldset. Expects a string value indicating the class for the object being composed. • ErrorMessage: specify the error message to return for an element in the case of a failed validation. Expects a string value. • Exclude: mark a property to exclude from the form or fieldset. This annotation does not require a value. • Filter: provide a specification for a filter to use on a given element. Expects an associative array of values, with a “name” key pointing to a string filter name, and an “options” key pointing to an associative array of filter options for the constructor: @Filter({"name": "Boolean", "options": {"casting":true}}). This annotation may be specified multiple times. • Flags: flags to pass to the fieldset or form composing an element or fieldset; these are usually used to specify the name or priority. The annotation expects an associative array: @Flags({"priority": 100}). • Hydrator: specify the hydrator class to use for this given form or fieldset. A string value is expected. • InputFilter: specify the input filter class to use for this given form or fieldset. A string value is expected. • Input: specify the input class to use for this given element. A string value is expected. • Name: specify the name of the current element, fieldset, or form. A string value is expected. • Options: options to pass to the fieldset or form that are used to inform behavior – things that are not attributes; e.g. labels, CAPTCHA adapters, etc. The annotation expects an associative array: @Options({"label": "Username:"}). • Required: indicate whether an element is required. A boolean value is expected. By default, all elements are required, so this annotation is mainly present to allow disabling a requirement. • Type: indicate the class to use for the current element, fieldset, or form. A string value is expected. • Validator: provide a specification for a validator to use on a given element. Expects an associative array of values, with a “name” key pointing to a string validator name, and an “options” key pointing to an associative array of validator options for the constructor: @Validator({"name": "StringLength", "options": {"min":3, "max": 25}}). This annotation may be specified multiple times. To use annotations, you simply include them in your class and/or property docblocks. Annotation names will be resolved according to the import statements in your class; as such, you can make them as long or as short as you want depending on what you import. Note: Form annotations require DoctrineCommon, which contains an annotation parsering engine. The simplest way to install DoctrineCommon is if you are using Composer; simply update your composer.json and add the following line to the require section: 115.9. Using Annotations 521
  • 560. Zend Framework 2 Documentation, Release 2.2.5 1 "doctrine/common": ">=2.1", Then run php composer.phar update to install the dependency. If you’re not using Composer, visit the Doctrine project website for more details on installation. Here’s a simple example. 1 use ZendFormAnnotation; 2 3 4 5 6 7 8 9 10 11 12 /** * @AnnotationName("user") * @AnnotationHydrator("ZendStdlibHydratorObjectProperty") */ class User { /** * @AnnotationExclude() */ public $id; 13 /** * @AnnotationFilter({"name":"StringTrim"}) * @AnnotationValidator({"name":"StringLength", "options":{"min":1, "max":25}}) * @AnnotationValidator({"name":"Regex", "options":{"pattern":"/^[a-zA-Z][a-zA-Z0-9_-]{0,24}$/"} * @AnnotationAttributes({"type":"text"}) * @AnnotationOptions({"label":"Username:"}) */ public $username; 14 15 16 17 18 19 20 21 22 /** * @AnnotationType("ZendFormElementEmail") * @AnnotationOptions({"label":"Your email address:"}) */ public $email; 23 24 25 26 27 28 } The above will hint to the annotation build to create a form with name “user”, which uses the hydrator ZendStdlibHydratorObjectProperty. That form will have two elements, “username” and “email”. The “username” element will have an associated input that has a StringTrim filter, and two validators: a StringLength validator indicating the username is between 1 and 25 characters, and a Regex validator asserting it follows a specific accepted pattern. The form element itself will have an attribute “type” with value “text” (a text element), and a label “Username:”. The “email” element will be of type ZendFormElementEmail, and have the label “Your email address:”. To use the above, we need ZendFormAnnotationAnnotationBuilder: 1 use ZendFormAnnotationAnnotationBuilder; 2 3 4 $builder = new AnnotationBuilder(); $form = $builder->createForm(’User’); At this point, you have a form with the appropriate hydrator attached, an input filter with the appropriate inputs, and all elements. Note: You’re not done In all likelihood, you’ll need to add some more elements to the form you construct. For example, you’ll want a submit 522 Chapter 115. Form Quick Start
  • 561. Zend Framework 2 Documentation, Release 2.2.5 button, and likely a CSRF-protection element. We recommend creating a fieldset with common elements such as these that you can then attach to the form you build via annotations. 115.9. Using Annotations 523
  • 562. Zend Framework 2 Documentation, Release 2.2.5 524 Chapter 115. Form Quick Start
  • 563. CHAPTER 116 Form Collections Often, fieldsets or elements in your forms will correspond to other domain objects. In some cases, they may correspond to collections of domain objects. In this latter case, in terms of user interfaces, you may want to add items dynamically in the user interface – a great example is adding tasks to a task list. This document is intended to demonstrate these features. To do so, we first need to define some domain objects that we’ll be using. 1 namespace ApplicationEntity; 2 3 4 5 6 7 8 class Product { /** * @var string */ protected $name; 9 10 11 12 13 /** * @var int */ protected $price; 14 15 16 17 18 /** * @var Brand */ protected $brand; 19 20 21 22 23 /** * @var array */ protected $categories; 24 25 26 27 28 29 30 31 32 33 /** * @param string $name * @return Product */ public function setName($name) { $this->name = $name; return $this; } 525
  • 564. Zend Framework 2 Documentation, Release 2.2.5 34 /** * @return string */ public function getName() { return $this->name; } 35 36 37 38 39 40 41 42 /** * @param int $price * @return Product */ public function setPrice($price) { $this->price = $price; return $this; } 43 44 45 46 47 48 49 50 51 52 /** * @return int */ public function getPrice() { return $this->price; } 53 54 55 56 57 58 59 60 /** * @param Brand $brand * @return Product */ public function setBrand(Brand $brand) { $this->brand = $brand; return $this; } 61 62 63 64 65 66 67 68 69 70 /** * @return Brand */ public function getBrand() { return $this->brand; } 71 72 73 74 75 76 77 78 /** * @param array $categories * @return Product */ public function setCategories(array $categories) { $this->categories = $categories; return $this; } 79 80 81 82 83 84 85 86 87 88 /** * @return array */ 89 90 91 526 Chapter 116. Form Collections
  • 565. Zend Framework 2 Documentation, Release 2.2.5 public function getCategories() { return $this->categories; } 92 93 94 95 96 } 97 98 99 100 101 102 103 class Brand { /** * @var string */ protected $name; 104 /** * @var string */ protected $url; 105 106 107 108 109 /** * @param string $name * @return Brand */ public function setName($name) { $this->name = $name; return $this; } 110 111 112 113 114 115 116 117 118 119 /** * @return string */ public function getName() { return $this->name; } 120 121 122 123 124 125 126 127 /** * @param string $url * @return Brand */ public function setUrl($url) { $this->url = $url; return $this; } 128 129 130 131 132 133 134 135 136 137 /** * @return string */ public function getUrl() { return $this->url; } 138 139 140 141 142 143 144 145 } 146 147 148 149 class Category { /** 527
  • 566. Zend Framework 2 Documentation, Release 2.2.5 * @var string */ protected $name; 150 151 152 153 /** * @param string $name * @return Category */ public function setName($name) { $this->name = $name; return $this; } 154 155 156 157 158 159 160 161 162 163 /** * @return string */ public function getName() { return $this->name; } 164 165 166 167 168 169 170 171 } As you can see, this is really simple code. A Product has two scalar properties (name and price), a OneToOne relationship (one product has one brand), and a OneToMany relationship (one product has many categories). 116.1 Creating Fieldsets The first step is to create three fieldsets. Each fieldset will contain all the fields and relationships for a specific entity. Here is the Brand fieldset: 1 namespace ApplicationForm; 2 3 4 5 6 use use use use ApplicationEntityBrand; ZendFormFieldset; ZendInputFilterInputFilterProviderInterface; ZendStdlibHydratorClassMethods as ClassMethodsHydrator; 7 8 9 10 11 12 class BrandFieldset extends Fieldset implements InputFilterProviderInterface { public function __construct() { parent::__construct(’brand’); 13 $this ->setHydrator(new ClassMethodsHydrator(false)) ->setObject(new Brand()) ; 14 15 16 17 18 $this->add(array( ’name’ => ’name’, ’options’ => array( ’label’ => ’Name of the brand’, ), ’attributes’ => array( ’required’ => ’required’, 19 20 21 22 23 24 25 528 Chapter 116. Form Collections
  • 567. Zend Framework 2 Documentation, Release 2.2.5 ), 26 )); 27 28 $this->add(array( ’name’ => ’url’, ’type’ => ’ZendFormElementUrl’, ’options’ => array( ’label’ => ’Website of the brand’, ), ’attributes’ => array( ’required’ => ’required’, ), )); 29 30 31 32 33 34 35 36 37 38 } 39 40 /** * @return array */ public function getInputFilterSpecification() { return array( ’name’ => array( ’required’ => true, ), ); } 41 42 43 44 45 46 47 48 49 50 51 52 } We can discover some new things here. As you can see, the fieldset calls the method setHydrator(), giving it a ClassMethods hydrator, and the setObject() method, giving it an empty instance of a concrete Brand object. When the data will be validated, the Form will automatically iterate through all the field sets it contains, and automatically populate the sub-objects, in order to return a complete entity. Also notice that the Url element has a type of ZendFormElementUrl. This information will be used to validate the input field. You don’t need to manually add filters or validators for this input as that element provides a reasonable input specification. Finally, getInputFilterSpecification() gives the specification for the remaining input (“name”), indicating that this input is required. Note that required in the array “attributes” (when elements are added) is only meant to add the “required” attribute to the form markup (and therefore has semantic meaning only). Here is the Category fieldset: 1 namespace ApplicationForm; 2 3 4 5 6 use use use use ApplicationEntityCategory; ZendFormFieldset; ZendInputFilterInputFilterProviderInterface; ZendStdlibHydratorClassMethods as ClassMethodsHydrator; 7 8 9 10 11 12 class CategoryFieldset extends Fieldset implements InputFilterProviderInterface { public function __construct() { parent::__construct(’category’); 13 14 15 $this ->setHydrator(new ClassMethodsHydrator(false)) 116.1. Creating Fieldsets 529
  • 568. Zend Framework 2 Documentation, Release 2.2.5 ->setObject(new Category()) 16 ; 17 18 $this->setLabel(’Category’); 19 20 $this->add(array( ’name’ => ’name’, ’options’ => array( ’label’ => ’Name of the category’, ), ’attributes’ => array( ’required’ => ’required’, ), )); 21 22 23 24 25 26 27 28 29 } 30 31 /** * @return array */ public function getInputFilterSpecification() { return array( ’name’ => array( ’required’ => true, ), ); } 32 33 34 35 36 37 38 39 40 41 42 43 } Nothing new here. And finally the Product fieldset: 1 namespace ApplicationForm; 2 3 4 5 6 use use use use ApplicationEntityProduct; ZendFormFieldset; ZendInputFilterInputFilterProviderInterface; ZendStdlibHydratorClassMethods as ClassMethodsHydrator; 7 8 9 10 11 12 class ProductFieldset extends Fieldset implements InputFilterProviderInterface { public function __construct() { parent::__construct(’product’); 13 $this ->setHydrator(new ClassMethodsHydrator(false)) ->setObject(new Product()) ; 14 15 16 17 18 $this->add(array( ’name’ => ’name’, ’options’ => array( ’label’ => ’Name of the product’, ), ’attributes’ => array( ’required’ => ’required’, ), 19 20 21 22 23 24 25 26 530 Chapter 116. Form Collections
  • 569. Zend Framework 2 Documentation, Release 2.2.5 )); 27 28 $this->add(array( ’name’ => ’price’, ’options’ => array( ’label’ => ’Price of the product’, ), ’attributes’ => array( ’required’ => ’required’, ), )); 29 30 31 32 33 34 35 36 37 38 $this->add(array( ’type’ => ’ApplicationFormBrandFieldset’, ’name’ => ’brand’, ’options’ => array( ’label’ => ’Brand of the product’, ), )); 39 40 41 42 43 44 45 46 $this->add(array( ’type’ => ’ZendFormElementCollection’, ’name’ => ’categories’, ’options’ => array( ’label’ => ’Please choose categories for this product’, ’count’ => 2, ’should_create_template’ => true, ’allow_add’ => true, ’target_element’ => array( ’type’ => ’ApplicationFormCategoryFieldset’, ), ), )); 47 48 49 50 51 52 53 54 55 56 57 58 59 } 60 61 /** * Should return an array specification compatible with * {@link ZendInputFilterFactory::createInputFilter()}. * * @return array */ public function getInputFilterSpecification() { return array( ’name’ => array( ’required’ => true, ), ’price’ => array( ’required’ => true, ’validators’ => array( array( ’name’ => ’Float’, ), ), ), ); } 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 } 116.1. Creating Fieldsets 531
  • 570. Zend Framework 2 Documentation, Release 2.2.5 We have a lot of new things here! First, notice how the brand element is added: we specify it to be of type ApplicationFormBrandFieldset. This is how you handle a OneToOne relationship. When the form is validated, the BrandFieldset will first be populated, and will return a Brand entity (as we have specified a ClassMethods hydrator, and bound the fieldset to a Brand entity using the setObject() method). This Brand entity will then be used to populate the Product entity by calling the setBrand() method. The next element shows you how to handle OneToMany relationship. The type is ZendFormElementCollection, which is a specialized element to handle such cases. As you can see, the name of the element (“categories”) perfectly matches the name of the property in the Product entity. This element has a few interesting options: • count: this is how many times the element (in this case a category) has to be rendered. We’ve set it to two in this examples. • should_create_template: if set to true, it will generate a template markup in a <span> element, in order to simplify adding new element on the fly (we will speak about this one later). • allow_add: if set to true (which is the default), dynamically added elements will be retrieved and validated; otherwise, they will be completely ignored. This, of course, depends on what you want to do. • target_element: this is either an element or, as this is the case in this example, an array that describes the element or fieldset that will be used in the collection. In this case, the target_element is a Category fieldset. 116.2 The Form Element So far, so good. We now have our field sets in place. But those are field sets, not forms. And only Form instances can be validated. So here is the form : 1 namespace ApplicationForm; 2 3 4 5 use ZendFormForm; use ZendInputFilterInputFilter; use ZendStdlibHydratorClassMethods as ClassMethodsHydrator; 6 7 8 9 10 11 class CreateProduct extends Form { public function __construct() { parent::__construct(’create_product’); 12 $this ->setAttribute(’method’, ’post’) ->setHydrator(new ClassMethodsHydrator(false)) ->setInputFilter(new InputFilter()) ; 13 14 15 16 17 18 $this->add(array( ’type’ => ’ApplicationFormProductFieldset’, ’options’ => array( ’use_as_base_fieldset’ => true, ), )); 19 20 21 22 23 24 25 $this->add(array( 26 532 Chapter 116. Form Collections
  • 571. Zend Framework 2 Documentation, Release 2.2.5 ’type’ => ’ZendFormElementCsrf’, ’name’ => ’csrf’, 27 28 )); 29 30 $this->add(array( ’name’ => ’submit’, ’attributes’ => array( ’type’ => ’submit’, ’value’ => ’Send’, ), )); 31 32 33 34 35 36 37 } 38 39 } CreateProduct is quite simple, as it only defines a Product fieldset, as well as some other useful fields (CSRF for security, and a Submit button). Notice the use_as_base_fieldset option. This option is here to say to the form: “hey, the object I bind to you is, in fact, bound to the fieldset that is the base fieldset.” This will be to true most of the times. What’s cool with this approach is that each entity can have its own Fieldset and can be reused. You describe the elements, the filters, and validators for each entity only once, and the concrete Form instance will only compose those fieldsets. You no longer have to add the “username” input to every form that deals with users! 116.3 The Controller Now, let’s create the action in the controller: 1 2 3 4 5 6 7 8 /** * @return array */ public function indexAction() { $form = new CreateProduct(); $product = new Product(); $form->bind($product); 9 $request = $this->getRequest(); if ($request->isPost()) { $form->setData($request->getPost()); 10 11 12 13 if ($form->isValid()) { var_dump($product); } 14 15 16 } 17 18 return array( ’form’ => $form, ); 19 20 21 22 } This is super easy. Nothing to do in the controllers. All the magic is done behind the scene. 116.3. The Controller 533
  • 572. Zend Framework 2 Documentation, Release 2.2.5 116.4 The View And finally, the view: 1 2 3 <?php $form->setAttribute(’action’, $this->url(’home’)) ->prepare(); 4 5 echo $this->form()->openTag($form); 6 7 $product = $form->get(’product’); 8 9 10 11 echo $this->formRow($product->get(’name’)); echo $this->formRow($product->get(’price’)); echo $this->formCollection($product->get(’categories’)); 12 13 $brand = $product->get(’brand’); 14 15 16 echo $this->formRow($brand->get(’name’)); echo $this->formRow($brand->get(’url’)); 17 18 19 echo $this->formHidden($form->get(’csrf’)); echo $this->formElement($form->get(’submit’)); 20 21 echo $this->form()->closeTag(); A few new things here : • the prepare() method. You must call it prior to rendering anything in the view (this function is only meant to be called in views, not in controllers). • the FormRow helper renders a label (if present), the input itself, and errors. • the FormCollection helper will iterate through every element in the collection, and render every element with the FormRow helper (you may specify an alternate helper if desired, using the setElementHelper() method on that FormCollection helper instance). If you need more control about the way you render your forms, you can iterate through the elements in the collection, and render them manually one by one. Here is the result: As you can see, collections are wrapped inside a fieldset, and every item in the collection is itself wrapped in the fieldset. In fact, the Collection element uses label for each item in the collection, while the label of the Collection element itself is used as the legend of the fieldset. You must have a label on every element in order to use this feature. If you don’t want the fieldset created, but just the elements within it, simply add a boolean false as the second 534 Chapter 116. Form Collections
  • 573. Zend Framework 2 Documentation, Release 2.2.5 parameter of the FormCollection view helper. If you validate, all elements will show errors (this is normal, as we’ve marked them as required). As soon as the form is valid, this is what we get : As you can see, the bound object is completely filled, not with arrays, but with objects! But that’s not all. 116.5 Adding New Elements Dynamically Remember the should_create_template? We are going to use it now. Often, forms are not completely static. In our case, let’s say that we don’t want only two categories, but we want the user to be able to add other ones at runtime. ZendForm has this capability. First, let’s see what it generates when we ask it to create a template: As you can see, the collection generates two fieldsets (the two categories) plus a span with a data-template attribute that contains the full HTML code to copy to create a new element in the collection. Of course __index__ (this is the placeholder generated) has to be changed to a valid value. Currently, we have 2 elements (categories[0] and categories[1], so __index__ has to be changed to 2. If you want, this placeholder (__index__ is the default) can be changed using the template_placeholder option key: 1 2 3 4 5 6 7 8 9 $this->add(array( ’type’ => ’ZendFormElementCollection’, ’name’ => ’categories’, ’options’ => array( ’label’ => ’Please choose categories for this product’, ’count’ => 2, ’should_create_template’ => true, ’template_placeholder’ => ’__placeholder__’, ’target_element’ => array( 116.5. Adding New Elements Dynamically 535
  • 574. Zend Framework 2 Documentation, Release 2.2.5 ’type’ => ’ApplicationFormCategoryFieldset’, 10 ), 11 ), 12 13 )); First, let’s add a small button “Add new category” anywhere in the form: 1 <button onclick="return add_category()">Add a new category</button> The add_category function is fairly simple: 1. First, count the number of elements we already have. 2. Get the template from the span‘s data-template attribute. 3. Change the placeholder to a valid index. 4. Add the element to the DOM. Here is the code: 1 2 3 4 5 <script> function add_category() { var currentCount = $(’form > fieldset > fieldset’).length; var template = $(’form > fieldset > span’).data(’template’); template = template.replace(/__index__/g, currentCount); 6 $(’form > fieldset’).append(template); 7 8 9 10 11 return false; } </script> (Note: the above example assumes $() is defined, and equivalent to jQuery’s $() function, Dojo’s dojo.query, etc.) One small remark about the template.replace: the example uses currentCount and not currentCount + 1, as the indices are zero-based (so, if we have two elements in the collection, the third one will have the index 2). Now, if we validate the form, it will automatically take into account this new element by validating it, filtering it and retrieving it: 536 Chapter 116. Form Collections
  • 575. Zend Framework 2 Documentation, Release 2.2.5 Of course, if you don’t want to allow adding elements in a collection, you must set the option allow_add to false. This way, even if new elements are added, they won’t be validated and hence, not added to the entity. Also, if we don’t want elements to be added, we don’t need the data template, either. Here’s how you do it: 1 2 3 4 5 6 7 8 9 10 11 12 13 $this->add(array( ’type’ => ’ZendFormElementCollection’, ’name’ => ’categories’, ’options’ => array( ’label’ => ’Please choose categories for this product’, ’count’ => 2, ’should_create_template’ => false, ’allow_add’ => false, ’target_element’ => array( ’type’ => ’ApplicationFormCategoryFieldset’, ), ), )); There are some limitations to this capability: • Although you can add new elements and remove them, you CANNOT remove more elements in a collection than the initial count (for instance, if your code specifies count == 2, you will be able to add a third one and remove it, but you won’t be able to remove any others. If the initial count is 2, you must have at least two elements. • Dynamically added elements have to be added at the end of the collection. They can be added anywhere (these elements will still be validated and inserted into the entity), but if the validation fails, this newly added element will be automatically be replaced at the end of the collection. 116.6 Validation groups for fieldsets and collection Validation groups allow you to validate a subset of fields. As an example, although the Brand entity has a URL property, we don’t want the user to specify it in the creation form (but may wish to later in the “Edit Product” form, for instance). Let’s update the view to remove the URL input: 116.6. Validation groups for fieldsets and collection 537
  • 576. Zend Framework 2 Documentation, Release 2.2.5 1 <?php 2 3 4 5 6 $form ->setAttribute(’action’, $this->url(’home’)) ->prepare() ; 7 8 echo $this->form()->openTag($form); 9 10 $product = $form->get(’product’); 11 12 13 14 echo $this->formRow($product->get(’name’)); echo $this->formRow($product->get(’price’)); echo $this->formCollection($product->get(’categories’)); 15 16 $brand = $product->get(’brand’); 17 18 echo $this->formRow($brand->get(’name’)); 19 20 21 echo $this->formHidden($form->get(’csrf’)); echo $this->formElement($form->get(’submit’)); 22 23 echo $this->form()->closeTag(); This is what we get: The URL input has disappeared, but even if we fill every input, the form won’t validate. In fact, this is normal. We specified in the input filter that the URL is a required field, so if the form does not have it, it won’t validate, even though we didn’t add it to the view! Of course, you could create a BrandFieldsetWithoutURL fieldset, but of course this is not recommended, as a lot of code will be duplicated. The solution: validation groups. A validation group is specified in a Form object (hence, in our case, in the 538 Chapter 116. Form Collections
  • 577. Zend Framework 2 Documentation, Release 2.2.5 CreateProduct form) by giving an array of all the elements we want to validate. Our CreateProduct now looks like this: 1 namespace ApplicationForm; 2 3 4 5 use ZendFormForm; use ZendInputFilterInputFilter; use ZendStdlibHydratorClassMethods as ClassMethodsHydrator; 6 7 8 9 10 11 class CreateProduct extends Form { public function __construct() { parent::__construct(’create_product’); 12 $this ->setAttribute(’method’, ’post’) ->setHydrator(new ClassMethodsHydrator()) ->setInputFilter(new InputFilter()) ; 13 14 15 16 17 18 $this->add(array( ’type’ => ’ApplicationFormProductFieldset’, ’options’ => array( ’use_as_base_fieldset’ => true, ), )); 19 20 21 22 23 24 25 $this->add(array( ’type’ => ’ZendFormElementCsrf’, ’name’ => ’csrf’, )); 26 27 28 29 30 $this->add(array( ’name’ => ’submit’, ’attributes’ => array( ’type’ => ’submit’, ’value’ => ’Send’, ), )); 31 32 33 34 35 36 37 38 $this->setValidationGroup(array( ’csrf’, ’product’ => array( ’name’, ’price’, ’brand’ => array( ’name’, ), ’categories’ => array( ’name’, ), ), )); 39 40 41 42 43 44 45 46 47 48 49 50 51 } 52 53 } Of course, don’t forget to add the CSRF element, as we want it to be validated too (but notice that I didn’t write the submit element, as we don’t care about it). You can recursively select the elements you want. 116.6. Validation groups for fieldsets and collection 539
  • 578. Zend Framework 2 Documentation, Release 2.2.5 There is one simple limitation currently: validation groups for collections are set on a per-collection basis, not perelement in a collection basis. This means you cannot say, “validate the name input for the first element of the categories collection, but don’t validate it for the second one.” But, honestly, this is really an edge-case. Now, the form validates (and the URL is set to null as we didn’t specify it). 540 Chapter 116. Form Collections
  • 579. CHAPTER 117 File Uploading Zend Framework provides support for file uploading by using features in ZendForm, ZendInputFilter, ZendValidator, ZendFilter, and ZendProgressBar. These reusable framework components provide a convenient and secure way for handling file uploads in your projects. Note: If the reader has experience with file uploading in Zend Framework v1.x, he/she will notice some major differences. Zend_FileTransfer has been deprecated in favor of using the standard ZF2 ZendForm and ZendInputFilter features. Note: The file upload features described here are specifically for forms using the POST method. Zend Framework itself does not currently provide specific support for handling uploads via the PUT method, but it is possible with PHP. See the PUT Method Support in the PHP documentation for more information. 117.1 Standard Example Handling file uploads is essentially the same as how you would use ZendForm for form processing, but with some slight caveats that will be described below. In this example we will: • Define a Form for backend validation and filtering. • Create a view template with a <form> containing a file input. • Process the form within a Controller action. 117.1.1 The Form and InputFilter Here we define a ZendFormElementFile input in a Form class named UploadForm. 1 // File: UploadForm.php 2 3 4 use ZendFormElement; use ZendFormForm; 5 541
  • 580. Zend Framework 2 Documentation, Release 2.2.5 6 7 8 9 10 11 12 class UploadForm extends Form { public function __construct($name = null, $options = array()) { parent::__construct($name, $options); $this->addElements(); } 13 public function addElements() { // File Input $file = new ElementFile(’image-file’); $file->setLabel(’Avatar Image Upload’) ->setAttribute(’id’, ’image-file’); $this->add($file); } 14 15 16 17 18 19 20 21 22 } The File element provides some automatic features that happen behind the scenes: • The form’s enctype will automatically be set to multipart/form-data when the form prepare() method is called. • The file element’s default input specification will create the correct Input type: ZendInputFilterFileInput. • The FileInput will automatically prepend an UploadFile Validator, to securely validate that the file is actually an uploaded file, and to report other types of upload errors to the user. 117.1.2 The View Template In the view template we render the <form>, a file input (with label and errors), and a submit button. 1 2 3 // File: upload-form.phtml <?php $form->prepare(); // The correct enctype is set here ?> <?php echo $this->form()->openTag($form); ?> 4 <div class="form-element"> <?php $fileElement = $form->get(’image-file’); ?> <?php echo $this->formLabel($fileElement); ?> <?php echo $this->formFile($fileElement); ?> <?php echo $this->formElementErrors($fileElement); ?> </div> 5 6 7 8 9 10 11 <button>Submit</button> 12 13 14 <?php echo $this->form()->closeTag(); ?> When rendered, the HTML should look similar to: 1 2 3 4 5 <form name="upload-form" id="upload-form" method="post" enctype="multipart/form-data"> <div class="form-element"> <label for="image-file">Avatar Image Upload</label> <input type="file" name="image-file" id="image-file"> </div> 6 7 8 <button>Submit</button> </form> 542 Chapter 117. File Uploading
  • 581. Zend Framework 2 Documentation, Release 2.2.5 117.1.3 The Controller Action For the final step, we will instantiate the UploadForm and process any postbacks in a Controller action. The form processing in the controller action will be similar to normal forms, except that you must merge the $_FILES information in the request with the other post data. 1 // File: MyController.php 2 3 4 5 public function uploadFormAction() { $form = new UploadForm(’upload-form’); 6 $request = $this->getRequest(); if ($request->isPost()) { // Make certain to merge the files info! $post = array_merge_recursive( $request->getPost()->toArray(), $request->getFiles()->toArray() ); 7 8 9 10 11 12 13 14 $form->setData($post); if ($form->isValid()) { $data = $form->getData(); // Form is valid, save the form! return $this->redirect()->toRoute(’upload-form/success’); } 15 16 17 18 19 20 } 21 22 return array(’form’ => $form); 23 24 } Upon a successful file upload, $form->getData() would return: 1 2 3 4 5 6 7 8 9 array(1) { ["image-file"] => array(5) { ["name"] => string(11) "myimage.png" ["type"] => string(9) "image/png" ["tmp_name"] => string(22) "/private/tmp/phpgRXd58" ["error"] => int(0) ["size"] => int(14908679) } } Note: It is suggested that you always use the ZendHttpPhpEnvironmentRequest object to retrieve and merge the $_FILES information with the form, instead of using $_FILES directly. This is due to how the file information is mapped in the $_FILES array: 1 2 3 4 5 6 7 8 9 10 // A $_FILES array with single input and multiple files: array(1) { ["image-file"]=>array(2) { ["name"]=>array(2) { [0]=>string(9)"file0.txt" [1]=>string(9)"file1.txt" } ["type"]=>array(2) { [0]=>string(10)"text/plain" [1]=>string(10)"text/html" 117.1. Standard Example 543
  • 582. Zend Framework 2 Documentation, Release 2.2.5 } 11 } 12 13 } 14 15 16 17 18 19 20 21 22 23 24 25 26 27 // How ZendHttpPhpEnvironmentRequest remaps the $_FILES array: array(1) { ["image-file"]=>array(2) { [0]=>array(2) { ["name"]=>string(9)"file0.txt" ["type"]=>string(10)"text/plain" }, [1]=>array(2) { ["name"]=>string(9)"file1.txt" ["type"]=>string(10)"text/html" } } } ZendInputFilterFileInput expects the file data be in this re-mapped array format. 117.2 File Post-Redirect-Get Plugin When using other standard form inputs (i.e. text, checkbox, select, etc.) along with file inputs in a Form, you can encounter a situation where some inputs may become invalid and the user must re-select the file and re-upload. PHP will delete uploaded files from the temporary directory at the end of the request if it has not been moved away or renamed. Re-uploading a valid file each time another form input is invalid is inefficient and annoying to users. One strategy to get around this is to split the form into multiple forms. One form for the file upload inputs and another for the other standard inputs. When you cannot separate the forms, the File Post-Redirect-Get Controller Plugin can be used to manage the file inputs and save off valid uploads until the entire form is valid. Changing our earlier example to use the fileprg plugin will require two changes. 1. Adding a RenameUpload filter to our form’s file input, with details on where the valid files should be stored: 1 // File: UploadForm.php 2 3 4 5 use ZendInputFilter; use ZendFormElement; use ZendFormForm; 6 7 8 9 10 11 12 13 14 class UploadForm extends Form { public function __construct($name = null, $options = array()) { parent::__construct($name, $options); $this->addElements(); $this->addInputFilter(); } 15 16 17 18 19 544 public function addElements() { // File Input $file = new ElementFile(’image-file’); Chapter 117. File Uploading
  • 583. Zend Framework 2 Documentation, Release 2.2.5 $file->setLabel(’Avatar Image Upload’) ->setAttribute(’id’, ’image-file’); $this->add($file); 20 21 22 } 23 24 public function addInputFilter() { $inputFilter = new InputFilterInputFilter(); 25 26 27 28 // File Input $fileInput = new InputFilterFileInput(’image-file’); $fileInput->setRequired(true); $fileInput->getFilterChain()->attachByName( ’filerenameupload’, array( ’target’ => ’./data/tmpuploads/avatar.png’, ’randomize’ => true, ) ); $inputFilter->add($fileInput); 29 30 31 32 33 34 35 36 37 38 39 40 $this->setInputFilter($inputFilter); 41 } 42 43 } The filerenameupload options above would cause an uploaded file to be renamed and moved to: ./data/tmpuploads/avatar_4b3403665fea6.png. See the RenameUpload filter documentation for more information on its supported options. 2. And, changing the Controller action to use the fileprg plugin: 1 // File: MyController.php 2 3 4 5 6 public function uploadFormAction() { $form = new UploadForm(’upload-form’); $tempFile = null; 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 $prg = $this->fileprg($form); if ($prg instanceof ZendHttpPhpEnvironmentResponse) { return $prg; // Return PRG redirect response } elseif (is_array($prg)) { if ($form->isValid()) { $data = $form->getData(); // Form is valid, save the form! return $this->redirect()->toRoute(’upload-form/success’); } else { // Form not valid, but file uploads might be valid... // Get the temporary file information to show the user in the view $fileErrors = $form->get(’image-file’)->getMessages(); if (empty($fileErrors)) { $tempFile = $form->get(’image-file’)->getValue(); } } } 25 26 27 return array( ’form’ => $form, 117.2. File Post-Redirect-Get Plugin 545
  • 584. Zend Framework 2 Documentation, Release 2.2.5 ’tempFile’ => $tempFile, 28 ); 29 } 30 Behind the scenes, the FilePRG plugin will: • Run the Form’s filters, namely the RenameUpload filter, to move the files out of temporary storage. • Store the valid POST data in the session across requests. • Change the required flag of any file inputs that had valid uploads to false. This is so that form resubmissions without uploads will not cause validation errors. Note: In the case of a partially valid form, it is up to the developer whether to notify the user that files have been uploaded or not. For example, you may wish to hide the form input and/or display the file information. These things would be implementation details in the view or in a custom view helper. Just note that neither the FilePRG plugin nor the formFile view helper will do any automatic notifications or view changes when files have been successfully uploaded. 117.3 HTML5 Multi-File Uploads With HTML5 we are able to select multiple files from a single file input using the multiple attribute. Not all browsers support multiple file uploads, but the file input will safely remain a single file upload for those browsers that do not support the feature. To enable multiple file uploads in Zend Framework, just set the file element’s multiple attribute to true: 1 // File: UploadForm.php 2 3 4 5 use ZendInputFilter; use ZendFormElement; use ZendFormForm; 6 7 8 9 10 11 12 13 14 class UploadForm extends Form { public function __construct($name = null, $options = array()) { parent::__construct($name, $options); $this->addElements(); $this->addInputFilter(); } 15 public function addElements() { // File Input $file = new ElementFile(’image-file’); $file->setLabel(’Avatar Image Upload’) ->setAttribute(’id’, ’image-file’) ->setAttribute(’multiple’, true); $this->add($file); } 16 17 18 19 20 21 22 23 24 // That’s it 25 public function addInputFilter() { $inputFilter = new InputFilterInputFilter(); 26 27 28 29 546 Chapter 117. File Uploading
  • 585. Zend Framework 2 Documentation, Release 2.2.5 // File Input $fileInput = new InputFilterFileInput(’image-file’); $fileInput->setRequired(true); 30 31 32 33 // You only need to define validators and filters // as if only one file was being uploaded. All files // will be run through the same validators and filters // automatically. $fileInput->getValidatorChain() ->attachByName(’filesize’, array(’max’ => 204800)) ->attachByName(’filemimetype’, array(’mimeType’ => ’image/png,image/x-png’)) ->attachByName(’fileimagesize’, array(’maxWidth’ => 100, ’maxHeight’ => 100)); 34 35 36 37 38 39 40 41 42 // All files will be renamed, i.e.: // ./data/tmpuploads/avatar_4b3403665fea6.png, // ./data/tmpuploads/avatar_5c45147660fb7.png $fileInput->getFilterChain()->attachByName( ’filerenameupload’, array( ’target’ => ’./data/tmpuploads/avatar.png’, ’randomize’ => true, ) ); $inputFilter->add($fileInput); 43 44 45 46 47 48 49 50 51 52 53 54 $this->setInputFilter($inputFilter); 55 } 56 57 } You do not need to do anything special with the validators and filters to support multiple file uploads. All of the files that are uploaded will have the same validators and filters run against them automatically (from logic within FileInput). You only need to define them as if one file was being uploaded. 117.4 Upload Progress While pure client-based upload progress meters are starting to become available with HTML5’s Progress Events, not all browsers have XMLHttpRequest level 2 support. For upload progress to work in a greater number of browsers (IE9 and below), you must use a server-side progress solution. ZendProgressBarUpload provides handlers that can give you the actual state of a file upload in progress. To use this feature you need to choose one of the Upload Progress Handlers (APC, uploadprogress, or Session) and ensure that your server setup has the appropriate extension or feature enabled. Note: For this example we will use PHP 5.4‘s Session progress handler PHP 5.4 is required and you may need to verify these php.ini settings for it to work: file_uploads = On post_max_size = 50M upload_max_filesize = 50M session.upload_progress.enabled = On session.upload_progress.freq = "1%" session.upload_progress.min_freq = "1" ; Also make certain ’upload_tmp_dir’ is writable 117.4. Upload Progress 547
  • 586. Zend Framework 2 Documentation, Release 2.2.5 When uploading a file with a form POST, you must also include the progress identifier in a hidden input. The File Upload Progress View Helpers provide a convenient way to add the hidden input based on your handler type. 1 2 3 4 // File: upload-form.phtml <?php $form->prepare(); ?> <?php echo $this->form()->openTag($form); ?> <?php echo $this->formFileSessionProgress(); // Must come before the file input! ?> 5 <div class="form-element"> <?php $fileElement = $form->get(’image-file’); ?> <?php echo $this->formLabel($fileElement); ?> <?php echo $this->formFile($fileElement); ?> <?php echo $this->formElementErrors($fileElement); ?> </div> 6 7 8 9 10 11 12 <button>Submit</button> 13 14 15 <?php echo $this->form()->closeTag(); ?> When rendered, the HTML should look similar to: 1 2 <form name="upload-form" id="upload-form" method="post" enctype="multipart/form-data"> <input type="hidden" id="progress_key" name="PHP_SESSION_UPLOAD_PROGRESS" value="12345abcde"> 3 <div class="form-element"> <label for="image-file">Avatar Image Upload</label> <input type="file" name="image-file" id="image-file"> </div> 4 5 6 7 8 9 10 <button>Submit</button> </form> There are a few different methods for getting progress information to the browser (long vs. short polling). Here we will use short polling since it is simpler and less taxing on server resources, though keep in mind it is not as responsive as long polling. When our form is submitted via AJAX, the browser will continuously poll the server for upload progress. The following is an example Controller action which provides the progress information: 1 // File: MyController.php 2 3 4 5 6 7 8 public function uploadProgressAction() { $id = $this->params()->fromQuery(’id’, null); $progress = new ZendProgressBarUploadSessionProgress(); return new ZendViewModelJsonModel($progress->getProgress($id)); } 9 10 11 12 13 14 15 16 17 // Returns JSON //{ // "total" // "current" // "rate" // "message" // "done" //} 548 : : : : : 204800, 10240, 1024, "10kB / 200kB", false Chapter 117. File Uploading
  • 587. Zend Framework 2 Documentation, Release 2.2.5 Warning: This is not the most efficient way of providing upload progress, since each polling request must go through the Zend Framework bootstrap process. A better example would be to use a standalone php file in the public folder that bypasses the MVC bootstrapping and only uses the essential ZendProgressBar adapters. Back in our view template, we will add the JavaScript to perform the AJAX POST of the form data, and to start a timeout interval for the progress polling. To keep the example code relatively short, we are using the jQuery Form plugin to do the AJAX form POST. If your project uses a different JavaScript framework (or none at all), this will hopefully at least illustrate the necessary high-level logic that would need to be performed. 1 2 // File: upload-form.phtml // ...after the form... 3 4 5 6 7 8 9 10 11 <!-- Twitter Bootstrap progress bar styles: http://twitter.github.com/bootstrap/components.html#progress --> <div id="progress" class="help-block"> <div class="progress progress-info progress-striped"> <div class="bar"></div> </div> <p></p> </div> 12 13 14 15 16 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script> <script src="/js/jquery.form.js"></script> <script> var progressInterval; 17 18 19 20 21 22 23 24 25 26 27 28 29 30 function getProgress() { // Poll our controller action with the progress id var url = ’/upload-form/upload-progress?id=’ + $(’#progress_key’).val(); $.getJSON(url, function(data) { if (data.status && !data.status.done) { var value = Math.floor((data.status.current / data.status.total) * 100); showProgress(value, ’Uploading...’); } else { showProgress(100, ’Complete!’); clearInterval(progressInterval); } }); } 31 32 33 34 35 function startProgress() { showProgress(0, ’Starting upload...’); progressInterval = setInterval(getProgress, 900); } 36 37 38 39 40 41 42 43 44 45 46 47 48 function showProgress(amount, message) { $(’#progress’).show(); $(’#progress .bar’).width(amount + ’%’); $(’#progress > p’).html(message); if (amount < 100) { $(’#progress .progress’) .addClass(’progress-info active’) .removeClass(’progress-success’); } else { $(’#progress .progress’) .removeClass(’progress-info active’) .addClass(’progress-success’); 117.4. Upload Progress 549
  • 588. Zend Framework 2 Documentation, Release 2.2.5 } 49 50 } 51 52 53 54 55 $(function() { // Register a ’submit’ event listener on the form to perform the AJAX POST $(’#upload-form’).on(’submit’, function(e) { e.preventDefault(); 56 if ($(’#image-file’).val() == ’’) { // No files selected, abort return; } 57 58 59 60 61 // Perform the submit //$.fn.ajaxSubmit.debug = true; $(this).ajaxSubmit({ beforeSubmit: function(arr, $form, options) { // Notify backend that submit is via ajax arr.push({ name: "isAjax", value: "1" }); }, success: function (response, statusText, xhr, $form) { clearInterval(progressInterval); showProgress(100, ’Complete!’); 62 63 64 65 66 67 68 69 70 71 72 // TODO: You’ll need to do some custom logic here to handle a successful // form post, and when the form is invalid with validation errors. if (response.status) { // TODO: Do something with a successful form post, like redirect // window.location.replace(response.redirect); } else { // Clear the file input, otherwise the same file gets re-uploaded // http://stackoverflow.com/a/1043969 var fileInput = $(’#image-file’); fileInput.replaceWith( fileInput.val(’’).clone( true ) ); 73 74 75 76 77 78 79 80 81 82 83 // TODO: Do something with these errors // showErrors(response.formErrors); 84 85 } }, error: function(a, b, c) { // NOTE: This callback is *not* called when the form is invalid. // It is called when the browser is unable to initiate or complete the ajax submit. // You will need to handle validation errors in the ’success’ callback. console.log(a, b, c); } 86 87 88 89 90 91 92 93 }); // Start the progress polling startProgress(); 94 95 96 97 98 99 }); }); </script> And finally, our Controller action can be modified to return form status and validation messages in JSON format if we see the ‘isAjax’ post parameter (which was set in the JavaScript just before submit): 1 // File: MyController.php 2 3 public function uploadFormAction() 550 Chapter 117. File Uploading
  • 589. Zend Framework 2 Documentation, Release 2.2.5 4 { $form = new UploadForm(’upload-form’); 5 6 $request = $this->getRequest(); if ($request->isPost()) { // Make certain to merge the files info! $post = array_merge_recursive( $request->getPost()->toArray(), $request->getFiles()->toArray() ); 7 8 9 10 11 12 13 14 $form->setData($post); if ($form->isValid()) { $data = $form->getData(); // Form is valid, save the form! if (!empty($post[’isAjax’])) { return new JsonModel(array( ’status’ => true, ’redirect’ => $this->url()->fromRoute(’upload-form/success’), ’formData’ => $data, )); } else { // Fallback for non-JS clients return $this->redirect()->toRoute(’upload-form/success’); } } else { if (!empty($post[’isAjax’])) { // Send back failure information via JSON return new JsonModel(array( ’status’ => false, ’formErrors’ => $form->getMessages(), ’formData’ => $form->getData(), )); } } 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 return array(’form’ => $form); 41 42 } 117.5 Additional Info Related documentation: • Form File Element • Form File View Helper • List of File Validators • List of File Filters • File Post-Redirect-Get Controller Plugin • ZendInputFilterFileInput • Upload Progress Handlers • Upload Progress View Helpers 117.5. Additional Info 551
  • 590. Zend Framework 2 Documentation, Release 2.2.5 External resources and blog posts from the community: • ZF2FileUploadExamples : A ZF2 module with several file upload examples. 552 Chapter 117. File Uploading
  • 591. CHAPTER 118 Advanced use of forms Beginning with Zend Framework 2.1, forms elements can be registered using a designated plugin manager of ZendServiceManager. This is similar to how view helpers, controller plugins, and filters are registered. This new feature has a number of benefits, especially when you need to handle complex dependencies in forms/fieldsets. This section describes all the benefits of this new architecture in ZF 2.1. 118.1 Short names The first advantage of pulling form elements from the service manager is that now you can use short names to create new elements through the factory. Therefore, this code: 1 2 3 4 $form->add(array( ’type’ => ’ZendFormElementEmail’, ’name’ => ’email’ )); can now be replaced by: 1 2 3 4 $form->add(array( ’type’ => ’Email’, ’name’ => ’email’ )); Each element provided out-of-the-box by Zend Framework 2 support this natively, so you can now make your initialization code more compact. 118.2 Creating custom elements ZendForm also supports custom form elements. To create a custom form element, make it extend the ZendFormElement class, or if you need a more specific one, extend one of the ZendFormElement classes. In the following we will show how to create a custom Phone element for entering phone numbers. It will extend ZendFormElement class and provide some default input rules. Our custom phone element could look something like this: 553
  • 592. Zend Framework 2 Documentation, Release 2.2.5 1 namespace ApplicationFormElement; 2 3 use ZendFormElement; 4 5 6 7 use ZendFormElement; use ZendInputFilterInputProviderInterface; use ZendValidatorRegex as RegexValidator; 8 9 10 11 12 13 14 class Phone extends Element implements InputProviderInterface { /** * @var ValidatorInterface */ protected $validator; 15 /** * Get a validator if none has been set. * * @return ValidatorInterface */ public function getValidator() { if (null === $this->validator) { $validator = new RegexValidator(’/^+?d{11,12}$/’); $validator->setMessage(’Please enter 11 or 12 digits only!’, RegexValidator::NOT_MATCH); 16 17 18 19 20 21 22 23 24 25 26 27 $this->validator = $validator; 28 } 29 30 return $this->validator; 31 } 32 33 /** * Sets the validator to use for this element * * @param ValidatorInterface $validator * @return ApplicationFormElementPhone */ public function setValidator(ValidatorInterface $validator) { $this->validator = $validator; return $this; } 34 35 36 37 38 39 40 41 42 43 44 45 /** * Provide default input rules for this element * * Attaches a phone number validator. * * @return array */ public function getInputSpecification() { return array( ’name’ => $this->getName(), ’required’ => true, ’filters’ => array( 46 47 48 49 50 51 52 53 54 55 56 57 58 554 Chapter 118. Advanced use of forms
  • 593. Zend Framework 2 Documentation, Release 2.2.5 array(’name’ => ’ZendFilterStringTrim’), ), ’validators’ => array( $this->getValidator(), ), 59 60 61 62 63 ); 64 } 65 66 } By implementing the ZendInputFilterInputProviderInterface interface, we are hinting to our form object that this element provides some default input rules for filtering and/or validating values. In this example the default input specification provides a ZendFilterStringTrim filter and a ZendValidatorRegex validator that validates that the value optionally has a + sign at the beginning and is followed by 11 or 12 digits. The easiest way of start using your new custom element in your forms is to use the custom element’s FQCN: 1 2 3 4 5 $form = new ZendFormForm(); $form->add(array( ’name’ => ’phone’, ’type’ => ’ApplicationFormElementPhone’, )); Or, if you are extending ZendFormForm: 1 namespace ApplicationForm; 2 3 use ZendFormForm; 4 5 6 7 8 9 class MyForm extends Form { public function __construct($name = null) { parent::__construct($name); 10 $this->add(array( ’name’ => ’phone’, ’type’ => ’ApplicationFormElementPhone’, )) 11 12 13 14 } 15 16 } If you don’t want to use the custom element’s FQCN, but rather a short name, as of Zend Framework 2.1 you can do so by adding them to the ZendFormFormElementManager plugin manager by utilising the getFormElementConfig function. Warning: To use custom elements with the FormElementManager needs a bit more work and most likely a change in how you write and use your forms. First, add the custom element to the plugin manager, in your Module.php class: 1 namespace Application; 2 3 use ZendModuleManagerFeatureFormElementProviderInterface; 4 5 6 7 8 class Module implements FormElementProviderInterface { public function getFormElementConfig() { 118.2. Creating custom elements 555
  • 594. Zend Framework 2 Documentation, Release 2.2.5 return array( ’invokables’ => array( ’phone’ => ’ApplicationFormElementPhone’ ) ); 9 10 11 12 13 } 14 15 } Or, you can do the same in your module.config.php file: 1 2 3 4 5 6 7 return array( ’form_elements’ => array( ’invokables’ => array( ’phone’ => ’ApplicationFormElementPhone’ ) ) ); You can use a factory instead of an invokable in order to handle dependencies in your elements/fieldsets/forms. And now comes the first catch. If you are creating your form class by extending ZendFormForm, you must not add the custom element in the __construct-or (as we have done in the previous example where we used the custom element’s FQCN), but rather in the init() method: 1 namespace ApplicationForm; 2 3 use ZendFormForm; 4 5 6 7 8 9 10 11 12 13 14 class MyForm extends Form { public function init() { $this->add(array( ’name’ => ’phone’, ’type’ => ’Phone’, )); } } The second catch is that you must not directly instantiate your form class, but rather get an instance of it through the ZendFormFormElementManager: 1 namespace ApplicationController; 2 3 use ZendMvcControllerAbstractActionController; 4 5 6 7 8 9 10 11 12 13 class IndexController extends AbstractActionController { public function indexAction() { $sl = $this->getServiceLocator(); $form = $sl->get(’FormElementManager’)->get(’ApplicationFormMyForm’); return array(’form’ => $form); } } The biggest gain of this is that you can easily override any built-in Zend Framework form elements if they do not fit your needs. For instance, if you want to create your own Email element instead of the standard one, you can simply 556 Chapter 118. Advanced use of forms
  • 595. Zend Framework 2 Documentation, Release 2.2.5 create your element and add it to the form element config with the same key as the element you want to replace: 1 namespace Application; 2 3 use ZendModuleManagerFeatureFormElementProviderInterface; 4 5 6 7 8 9 10 11 12 13 14 15 class Module implements FormElementProviderInterface { public function getFormElementConfig() { return array( ’invokables’ => array( ’Email’ => ’ApplicationFormElementMyEmail’ ) ); } } Now, whenever you’ll create an element whose type is ‘Email’, it will create the custom Email element instead of the built-in one. Note: if you want to be able to use both the built-in one and your own one, you can still provide the FQCN of the element, i.e. ZendFormElementEmail. As you can see here, we first get the form manager (that we modified in our Module.php class), and create the form by specifying the fully qualified class name of the form. Please note that you don’t need to add ApplicationFormMyForm to the invokables array. If it is not specified, the form manager will just instantiate it directly. In short, to create your own form elements (or even reusable fieldsets !) and be able to use them in your form using the short-name notation, you need to: 1. Create your element (like you did before). 2. Add it to the form element manager by defining the getFormElementConfig, exactly like using getServiceConfig() and getControllerConfig. 3. Make sure the custom form element is not added in the form’s __construct-or, but rather in it’s init() method, or after getting an instance of the form. 4. Create your form through the form element manager instead of directly instantiating it. 118.3 Handling dependencies One of the most complex issues in ZendForm 2.0 was dependency management. For instance, a very frequent use case is a form that creates a fieldset, that itself need access to the database to populate a Select element. Previously in such a situation, you would either rely on the Registry using the Singleton pattern, or either you would “transfer” the dependency from controller to form, and from form to fieldset (and even from fieldset to another fieldset if you have a complex form). This was ugly and not easy to use. Hopefully, ZendServiceManager solves this use case in an elegant manner. For instance, let’s say that a form create a fieldset called AlbumFieldset: 1 namespace ApplicationForm; 2 3 use ZendFormForm; 4 118.3. Handling dependencies 557
  • 596. Zend Framework 2 Documentation, Release 2.2.5 5 6 7 8 9 10 11 12 13 14 class CreateAlbum extends Form { public function init() { $this->add(array( ’name’ => ’album’, ’type’ => ’AlbumFieldset’ )); } } Let’s now create the AlbumFieldset that depends on an AlbumTable object that allows you to fetch albums from the database. 1 namespace ApplicationForm; 2 3 4 use AlbumModelAlbumTable; use ZendFormFieldset; 5 6 7 8 9 10 11 12 13 class AlbumFieldset extends Fieldset { public function __construct(AlbumTable $albumTable) { // Add any elements that need to fetch data from database // using the album table ! } } For this to work, you need to add a line to the form element manager by adding an element to your Module.php class: 1 namespace Application; 2 3 4 use ApplicationFormAlbumFieldset; use ZendModuleManagerFeatureFormElementProviderInterface; 5 6 7 8 9 10 11 12 13 14 class Module implements FormElementProviderInterface { public function getFormElementConfig() { return array( ’factories’ => array( ’AlbumFieldset’ => function($sm) { // I assume here that the AlbumModelAlbumTable // dependency have been defined too 15 $serviceLocator = $sm->getServiceLocator(); $albumTable = $serviceLocator->get(’AlbumModelAlbumTable’); $fieldset = new AlbumFieldset($albumTable); return $fieldset; 16 17 18 19 } 20 ) 21 ); 22 } 23 24 } Create your form using the form element manager instead of directly instantiating it: 1 2 public function testAction() { 558 Chapter 118. Advanced use of forms
  • 597. Zend Framework 2 Documentation, Release 2.2.5 $formManager = $this->serviceLocator->get(’FormElementManager’); $form = $formManager->get(’ApplicationFormCreateAlbum’); 3 4 5 } Finally, to use your fieldset in a view you need to use the formCollection function. 1 2 3 echo $this->form()->openTag($form); echo $this->formCollection($form->get(’album’)); echo $this->form()->closeTag(); Et voil! The dependency will be automatically handled by the form element manager, and you don’t need to create the AlbumTable in your controller, transfer it to the form, which itself passes it over to the fieldset. 118.4 The specific case of initializers In the previous example, we explicitly defined the dependency in the constructor of the AlbumFieldset class. However, in some cases, you may want to use an initializer (like ZendServiceManagerServiceLocatorAwareInterface) to inject a specific object to all your forms/fieldsets/elements. The problem with initializers is that they are injected AFTER the construction of the object, which means that if you need this dependency when you create elements, it won’t be available yet. For instance, this example won’t work: 1 namespace ApplicationForm; 2 3 4 5 6 use use use use AlbumModel; ZendFormFieldset; ZendServiceManagerServiceLocatorAwareInterface; ZendServiceManagerServiceLocatorInterface; 7 8 9 10 class AlbumFieldset extends Fieldset implements ServiceLocatorAwareInterface { protected $serviceLocator; 11 public function __construct() { // Here, $this->serviceLocator is null because it has not been // injected yet, as initializers are run after __construct } 12 13 14 15 16 17 public function setServiceLocator(ServiceLocatorInterface $sl) { $this->serviceLocator = $sl; } 18 19 20 21 22 public function getServiceLocator() { return $this->serviceLocator; } 23 24 25 26 27 } Thankfully, there is an easy workaround: every form element now implements the new interface ZendStdlibInitializableInterface, that defines a single init() function. In the context of form elements, this init() function is automatically called once all the dependencies (including all initializers) are resolved. Therefore, the previous example can be rewritten as such: 118.4. The specific case of initializers 559
  • 598. Zend Framework 2 Documentation, Release 2.2.5 1 namespace ApplicationForm; 2 3 4 5 6 use use use use AlbumModel; ZendFormFieldset; ZendServiceManagerServiceLocatorAwareInterface; ZendServiceManagerServiceLocatorInterface; 7 8 9 10 class AlbumFieldset extends Fieldset implements ServiceLocatorAwareInterface { protected $serviceLocator; 11 public function init() { // Here, we have $this->serviceLocator !! } 12 13 14 15 16 public function setServiceLocator(ServiceLocatorInterface $sl) { $this->serviceLocator = $sl; } 17 18 19 20 21 public function getServiceLocator() { return $this->serviceLocator; } 22 23 24 25 26 } 560 Chapter 118. Advanced use of forms
  • 599. CHAPTER 119 Form Elements 119.1 Introduction A set of specialized elements are provided for accomplishing application-centric tasks. These include several HTML5 input elements with matching server-side validators, the Csrf element (to prevent Cross Site Request Forgery attacks), and the Captcha element (to display and validate CAPTCHAs). A Factory is provided to facilitate creation of elements, fieldsets, forms, and the related input filter. See the ZendForm Quick Start for more information. 119.2 Element Base Class ZendFormElement is a base class for all specialized elements and ZendFormFieldset. Basic Usage At the bare minimum, each element or fieldset requires a name. You will also typically provide some attributes to hint to the view layer how it might render the item. 1 2 use ZendFormElement; use ZendFormForm; 3 4 5 6 7 8 9 10 $username = new ElementText(’username’); $username ->setLabel(’Username’) ->setAttributes(array( ’class’ => ’username’, ’size’ => ’30’, )); 11 12 13 14 15 16 17 $password = new ElementPassword(’password’); $password ->setLabel(’Password’) ->setAttributes(array( ’size’ => ’30’, )); 561
  • 600. Zend Framework 2 Documentation, Release 2.2.5 18 19 20 21 22 $form = new Form(’my-form’); $form ->add($username) ->add($password); Public Methods setName(string $name) Set the name for this element. getName() Return the name for this element. Return type string setValue(string $value) Set the value for this element. getValue() Return the value for this element. Return type string setLabel(string $label) Set the label content for this element. getLabel() Return the label content for this element. Return type string setLabelAttributes(array $labelAttributes) Set the attributes to use with the label. getLabelAttributes() Return the attributes to use with the label. Return type array setOptions(array $options) Set options for an element. Accepted options are: "label" and "label_attributes", which call setLabel and setLabelAttributes, respectively. getOptions() Get defined options for an element Return type array getOption(string $option) Return the specified option, if defined. If it’s not defined, returns null. Return type null|mixed setAttribute(string $key, mixed $value) Set a single element attribute. getAttribute(string $key) Retrieve a single element attribute. Return type mixed 562 Chapter 119. Form Elements
  • 601. Zend Framework 2 Documentation, Release 2.2.5 removeAttribute(string $key) Remove a single attribute hasAttribute(string $key) Check if a specific attribute exists for this element. Return type boolean setAttributes(array|Traversable $arrayOrTraversable) Set many attributes at once. Implementation will decide if this will overwrite or merge. getAttributes() Retrieve all attributes at once. Return type array|Traversable removeAttributes(array $keys) Remove many attributes at once clearAttributes() Clear all attributes for this element. setMessages(array|Traversable $messages) Set a list of messages to report when validation fails. getMessages() Returns a list of validation failure messages, if any. Return type array|Traversable 119.3 Standard Elements 119.3.1 Button ZendFormElementButton represents a button ZendFormViewHelperFormButton view helper. form input. It can be used with the ZendFormElementButton extends from ZendFormElement. Basic Usage This element automatically adds a type attribute of value button. 1 2 use ZendFormElement; use ZendFormForm; 3 4 5 6 $button = new ElementButton(’my-button’); $button->setLabel(’My Button’) ->setValue(’foo’); 7 8 9 $form = new Form(’my-form’); $form->add($button); 119.3. Standard Elements 563
  • 602. Zend Framework 2 Documentation, Release 2.2.5 119.3.2 Captcha ZendFormElementCaptcha can be used with forms where authenticated users are not necessary, but you want to prevent spam submissions. It is paired with one of the ZendFormViewHelperCaptcha* view helpers that matches the type of CAPTCHA adapter in use. Basic Usage A CAPTCHA adapter must be attached in order for validation to be included in the element’s input filter specification. See the section on Zend CAPTCHA Adapters for more information on what adapters are available. 1 2 3 use ZendCaptcha; use ZendFormElement; use ZendFormForm; 4 5 6 7 8 $captcha = new ElementCaptcha(’captcha’); $captcha ->setCaptcha(new CaptchaDumb()) ->setLabel(’Please verify you are human’); 9 10 11 $form = new Form(’my-form’); $form->add($captcha); Here is with the array notation: 1 2 use ZendCaptcha; use ZendFormForm; 3 4 5 6 7 8 9 10 11 12 $form = new Form(’my-form’); $form->add(array( ’type’ => ’ZendFormElementCaptcha’, ’name’ => ’captcha’, ’options’ => array( ’label’ => ’Please verify you are human’, ’captcha’ => new CaptchaDumb(), ), )); Public Methods The following methods are in addition to the inherited methods of ZendFormElement. setCaptcha(array|ZendCaptchaAdapterInterface $captcha) Set the CAPTCHA adapter for this element. If $captcha is an array, ZendCaptchaFactory::factory() will be run to create the adapter from the array configuration. getCaptcha() Return the CAPTCHA adapter for this element. Return type ZendCaptchaAdapterInterface getInputSpecification() Returns a input filter specification, which includes a ZendFilterStringTrim filter, and a CAPTCHA validator. Return type array 564 Chapter 119. Form Elements
  • 603. Zend Framework 2 Documentation, Release 2.2.5 119.3.3 Checkbox ZendFormElementCheckbox is meant to be paired with the ZendFormViewHelperFormCheckbox for HTML inputs with type checkbox. This element adds an InArray validator to its input filter specification in order to validate on the server if the checkbox contains either the checked value or the unchecked value. Basic Usage This element automatically adds a "type" attribute of value "checkbox". 1 2 use ZendFormElement; use ZendFormForm; 3 4 5 6 7 8 $checkbox = new ElementCheckbox(’checkbox’); $checkbox->setLabel(’A checkbox’); $checkbox->setUseHiddenElement(true); $checkbox->setCheckedValue("good"); $checkbox->setUncheckedValue("bad"); 9 10 11 $form = new Form(’my-form’); $form->add($checkbox); Using the array notation: 1 use ZendFormForm; 2 3 4 5 6 7 8 9 10 11 12 13 $form = new Form(’my-form’); $form->add(array( ’type’ => ’ZendFormElementCheckbox’, ’name’ => ’checkbox’, ’options’ => array( ’label’ => ’A checkbox’, ’use_hidden_element’ => true, ’checked_value’ => ’good’, ’unchecked_value’ => ’bad’ ) )); Public Methods The following methods are in addition to the inherited methods of ZendFormElement . setOptions(array $options) Set options for an element of type Checkbox. Accepted options, in addition to the inherited options of ZendFormElement , are: "use_hidden_element", "checked_value" and "unchecked_value" , which call setUseHiddenElement, setCheckedValue and setUncheckedValue , respectively. setUseHiddenElement(boolean $useHiddenElement) If set to true (which is default), the view helper will generate a hidden element that contains the unchecked value. Therefore, when using custom unchecked value, this option have to be set to true. useHiddenElement() Return if a hidden element is generated. Return type boolean 119.3. Standard Elements 565
  • 604. Zend Framework 2 Documentation, Release 2.2.5 setCheckedValue(string $checkedValue) Set the value to use when the checkbox is checked. getCheckedValue() Return the value used when the checkbox is checked. Return type string setUncheckedValue(string $uncheckedValue) Set the value to use when the checkbox is unchecked. use_hidden_element is set to true. For this to work, you must make sure that getUncheckedValue() Return the value used when the checkbox is unchecked. Return type string getInputSpecification() Returns a input filter specification, which includes a ZendValidatorInArray to validate if the value is either checked value or unchecked value. Return type array isChecked() Checks if the checkbox is checked. Return type boolean setChecked(bool $value) Checks or unchecks the checkbox. 119.3.4 Collection Sometimes, you may want to add input (or a set of inputs) multiple times, either because you don’t want to duplicate code, or because you do not know in advance how many elements you will need (in the case of elements dynamically added to a form using JavaScript, for instance). For more information about Collection, please refer to the Form Collections tutorial. ZendFormElementCollection is meant to be paired with the ZendFormViewHelperFormCollection. Basic Usage 1 2 use ZendFormElement; use ZendFormForm; 3 4 5 6 7 8 $colors = new ElementCollection(’collection’); $colors->setLabel(’Colors’); $colors->setCount(2); $colors->setTargetElement(new ElementColor()); $colors->setShouldCreateTemplate(true); 9 10 11 $form = new Form(’my-form’); $form->add($colors); Using the array notation: 566 Chapter 119. Form Elements
  • 605. Zend Framework 2 Documentation, Release 2.2.5 1 2 use ZendFormElement; use ZendFormForm; 3 4 5 6 7 8 9 10 11 12 13 $form = new Form(’my-form’); $form->add(array( ’type’ => ’ZendFormElementCollection’, ’options’ => array( ’label’ => ’Colors’, ’count’ => 2, ’should_create_template’ => true, ’target_element’ => new ElementColor() ) )); Public Methods The following methods are in addition to the inherited methods of ZendFormElement . setOptions(array $options) Set options for an element of type Collection. Accepted options, in addition to the inherited options of ZendFormElement , are: "target_element", "count", "allow_add", "allow_remove", "should_create_template" and "template_placeholder". Those option keys respectively call call setTargetElement, setCount, setAllowAdd, setAllowRemove, setShouldCreateTemplate and setTemplatePlaceholder. allowObjectBinding(object $object) Checks if the object can be set in this fieldset. Return type bool setObject(array|Traversable $object) Set the object used by the hydrator. In this case the “object” is a collection of objects. populateValues(array|Traversable $data) Populate values allowValueBinding() Checks if this fieldset can bind data Return type bool setCount($count) Defines how many times the target element will ZendFormViewHelperFormCollection view helper. getCount() Return the number of times the target element will ZendFormViewHelperFormCollection view helper. be be initially initially rendered rendered by the by the Return type integer setTargetElement($elementOrFieldset) This function either takes an ZendFormElementInterface, ZendFormFieldsetInterface instance or an array to pass to the form factory. When the Collection element will be validated, the input filter will be retrieved from this target element and be used to validate each element in the collection. getTargetElement() Return the target element used by the collection. Return type ElementInterface | null 119.3. Standard Elements 567
  • 606. Zend Framework 2 Documentation, Release 2.2.5 setAllowAdd($allowAdd) If allowAdd is set to true (which is the default), new elements added dynamically in the form (using JavaScript, for instance) will also be validated and retrieved. allowAdd() Return if new elements can be dynamically added in the collection. Return type boolean setAllowRemove($allowRemove) If allowRemove is set to true (which is the default), new elements added dynamically in the form (using JavaScript, for instance) will be allowed to be removed. allowRemove() Return if new elements can be dynamically removed from the collection. Return type boolean setShouldCreateTemplate($shouldCreateTemplate) If shouldCreateTemplate is set to true (defaults to false), a <span> element will be generated by the ZendFormViewHelperFormCollection view helper. This non-semantic span element contains a single data-template HTML5 attribute whose value is the whole HTML to copy to create a new element in the form. The template is indexed using the templatePlaceholder value. shouldCreateTemplate() Return if a template should be created. Return type boolean setTemplatePlaceholder($templatePlaceholder) Set the template placeholder (defaults to __index__) used to index element in the template. getTemplatePlaceholder() Returns the template placeholder used to index element in the template. Return type string getTemplateElement() Get a template element used for rendering purposes only Return type null|ElementInterface|FieldsetInterface prepareElement() Prepare the collection by adding a dummy template element if the user want one prepareFieldset() If both count and targetElement are set, add them to the fieldset 119.3.5 Csrf ZendFormElementCsrf pairs with the ZendFormViewHelperFormHidden to provide protection from CSRF attacks on forms, ensuring the data is submitted by the user session that generated the form and not by a rogue script. Protection is achieved by adding a hash element to a form and verifying it when the form is submitted. Basic Usage This element automatically adds a "type" attribute of value "hidden". 568 Chapter 119. Form Elements
  • 607. Zend Framework 2 Documentation, Release 2.2.5 1 2 use ZendFormElement; use ZendFormForm; 3 4 $csrf = new ElementCsrf(’csrf’); 5 6 7 $form = new Form(’my-form’); $form->add($csrf); You can change the options of the CSRF validator using the setCsrfValidatorOptions function, or by using the "csrf_options" key. Here is an example using the array notation: 1 use ZendFormForm; 2 3 4 5 6 7 8 9 10 11 12 $form = new Form(’my-form’); $form->add(array( ’type’ => ’ZendFormElementCsrf’, ’name’ => ’csrf’, ’options’ => array( ’csrf_options’ => array( ’timeout’ => 600 ) ) )); Public Methods The following methods are in addition to the inherited methods of ZendFormElement. getInputSpecification() Returns a input filter specification, which includes a ZendFilterStringTrim filter and a ZendValidatorCsrf to validate the CSRF value. Return type array setCsrfValidatorOptions(array $options) Set the options that are used by the CSRF validator. getCsrfValidatorOptions() Get the options that are used by the CSRF validator. Return type array setCsrfValidator(ZendValidatorCsrf $validator) Override the default CSRF validator by setting another one. getCsrfValidator() Get the CSRF validator. Return type ZendValidatorCsrf 119.3.6 File ZendFormElementFile represents a form file input and provides a default input specification with a type of FileInput (important for handling validators and filters correctly). It can be used with the ZendFormViewHelperFormFile view helper. ZendFormElementFile extends from ZendFormElement. 119.3. Standard Elements 569
  • 608. Zend Framework 2 Documentation, Release 2.2.5 Basic Usage This element automatically adds a "type" attribute of value "file". It will also set the form’s enctype to multipart/form-data during $form->prepare(). 1 2 use ZendFormElement; use ZendFormForm; 3 4 5 6 // Single file upload $file = new ElementFile(’file’); $file->setLabel(’Single file input’); 7 8 9 10 11 // HTML5 multiple file upload $multiFile = new ElementFile(’multi-file’); $multiFile->setLabel(’Multi file input’) ->setAttribute(’multiple’, true); 12 13 14 15 $form = new Form(’my-file’); $form->add($file) ->add($multiFile); 119.3.7 Hidden ZendFormElementHidden represents a hidden ZendFormViewHelperFormHidden view helper. form input. It can be used with the ZendFormElementHidden extends from ZendFormElement. Basic Usage This element automatically adds a "type" attribute of value "hidden". 1 2 use ZendFormElement; use ZendFormForm; 3 4 5 $hidden = new ElementHidden(’my-hidden’); $hidden->setValue(’foo’); 6 7 8 $form = new Form(’my-form’); $form->add($hidden); Here is with the array notation: 1 use ZendFormForm; 2 3 4 5 6 7 8 9 10 $form = new Form(’my-form’); $form->add(array( ’type’ => ’ZendFormElementHidden’, ’name’ => ’my-hidden’, ’attributes’ => array( ’value’ => ’foo’ ) )); 570 Chapter 119. Form Elements
  • 609. Zend Framework 2 Documentation, Release 2.2.5 119.3.8 Image ZendFormElementImage represents a image button form input. ZendFormViewHelperFormImage view helper. It can be used with the ZendFormElementImage extends from ZendFormElement. Basic Usage This element automatically adds a "type" attribute of value "image". 1 2 use ZendFormElement; use ZendFormForm; 3 4 5 $image = new ElementImage(’my-image’); $image->setAttribute(’src’, ’http://my.image.url’); // Src attribute is required 6 7 8 $form = new Form(’my-form’); $form->add($image); 119.3.9 Month Select ZendFormElementMonthSelect is meant to be paired with the ZendFormViewHelperFormMonthSelect. This element creates two select elements, where the first one is populated with months and the second is populated with years. By default, it sets 100 years in the past for the year element, starting with the current year. Basic Usage 1 2 use ZendFormElement; use ZendFormForm; 3 4 5 6 $monthYear = new ElementMonthSelect(’monthyear’); $monthYear->setLabel(’Select a month and a year’); $monthYear->setMinYear(1986); 7 8 9 $form = new Form(’dateselect’); $form->add($monthYear); Using the array notation: 1 use ZendFormForm; 2 3 4 5 6 7 8 9 10 11 $form = new Form(’dateselect’); $form->add(array( ’type’ => ’ZendFormElementMonthSelect’, ’name’ => ’monthyear’, ’options’ => array( ’label’ => ’Select a month and a year’, ’min_year’ => 1986, ) )); 119.3. Standard Elements 571
  • 610. Zend Framework 2 Documentation, Release 2.2.5 Public Methods The following methods are in addition to the inherited methods of ZendFormElement. getMonthElement() Returns the Select element that is used for the months part. Return type ZendFormElementSelect getYearElement() Returns the Select element that is used for the years part. Return type ZendFormElementSelect setMonthAttributes(array $monthAttributes) Set attributes on the Select element that is used for the months part. getMonthAttributes() Get attributes on the Select element that is used for the months part. Return type array setYearAttributes(array $yearAttributes) Set attributes on the Select element that is used for the years part. getYearAttributes() Get attributes on the Select element that is used for the years part. Return type array setMinYear(int $minYear) Set the minimum year. getMinYear() Get the minimum year. setMaxYear(int $maxYear) Set the maximum year. getMaxYear() Get the maximum year. setValue(mixed $value) Set the value for the MonthSelect element. If the value is an instance of DateTime, it will use the month and year values from that date. Otherwise, the value should be an associative array with the month key for the month value, and with the year key for the year value. 119.3.10 MultiCheckbox ZendFormElementMultiCheckbox is meant to be paired with the ZendFormViewHelperFormMultiCheckbox for HTML inputs with type checkbox. This element adds an InArray validator to its input filter specification in order to validate on the server if the checkbox contains values from the multiple checkboxes. Basic Usage This element automatically adds a "type" attribute of value "checkbox" for every checkboxes. 572 Chapter 119. Form Elements
  • 611. Zend Framework 2 Documentation, Release 2.2.5 1 2 use ZendFormElement; use ZendFormForm; 3 4 5 6 7 8 9 10 $multiCheckbox = new ElementMultiCheckbox(’multi-checkbox’); $multiCheckbox->setLabel(’What do you like ?’); $multiCheckbox->setValueOptions(array( ’0’ => ’Apple’, ’1’ => ’Orange’, ’2’ => ’Lemon’ )); 11 12 13 $form = new Form(’my-form’); $form->add($multiCheckbox); Using the array notation: 1 use ZendFormForm; 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $form = new Form(’my-form’); $form->add(array( ’type’ => ’ZendFormElementMultiCheckbox’, ’name’ => ’multi-checkbox’, ’options’ => array( ’label’ => ’What do you like ?’, ’value_options’ => array( ’0’ => ’Apple’, ’1’ => ’Orange’, ’2’ => ’Lemon’, ), ) )); Public Methods The following methods are in addition to the inherited methods of ZendFormElementCheckbox . setOptions(array $options) Set options for an element of type Checkbox. Accepted options, in addition to the inherited options of ZendFormElementCheckbox , are: "value_options", which call setValueOptions. setValueOptions(array $options) Set the value options for every checkbox of the multi-checkbox. The array must contain a key => value for every checkbox. getValueOptions() Return the value options. Return type array 119.3.11 Password ZendFormElementPassword represents a password form input. ZendFormViewHelperFormPassword view helper. It can be used with the ZendFormElementPassword extends from ZendFormElement. 119.3. Standard Elements 573
  • 612. Zend Framework 2 Documentation, Release 2.2.5 Basic Usage This element automatically adds a "type" attribute of value "password". 1 2 use ZendFormElement; use ZendFormForm; 3 4 5 $password = new ElementPassword(’my-password’); $password->setLabel(’Enter your password’); 6 7 8 $form = new Form(’my-form’); $form->add($password); 119.3.12 Radio ZendFormElementRadio is meant to be paired with the ZendFormViewHelperFormRadio for HTML inputs with type radio. This element adds an InArray validator to its input filter specification in order to validate on the server if the value is contains within the radio value elements. Basic Usage This element automatically adds a "type" attribute of value "radio" for every radio. use ZendFormElement; use ZendFormForm; 1 2 3 $radio = new ElementRadio(’gender’); $radio->setLabel(’What is your gender ?’); $radio->setValueOptions(array( ’0’ => ’Female’, ’1’ => ’Male’, )); 4 5 6 7 8 9 10 $form = new Form(’my-form’); $form->add($radio); 11 12 Using the array notation: 1 use ZendFormForm; 2 $form = new Form(’my-form’); $form->add(array( ’type’ => ’ZendFormElementRadio’, ’name’ => ’gender’, ’options’ => array( ’label’ => ’What is your gender ?’, ’value_options’ => array( ’0’ => ’Female’, ’1’ => ’Male’, ), ) )); 3 4 5 6 7 8 9 10 11 12 13 14 574 Chapter 119. Form Elements
  • 613. Zend Framework 2 Documentation, Release 2.2.5 Public Methods All the methods from the inherited methods of ZendFormElementMultiCheckbox are also available for this element. 119.3.13 Select ZendFormElementSelect is meant to be paired with the ZendFormViewHelperFormSelect for HTML inputs with type select. This element adds an InArray validator to its input filter specification in order to validate on the server if the selected value belongs to the values. This element can be used as a multi-select element by adding the “multiple” HTML attribute to the element. Basic Usage This element automatically adds a "type" attribute of value "select". 1 2 use ZendFormElement; use ZendFormForm; 3 4 5 6 7 8 9 10 11 $select = new ElementSelect(’language’); $select->setLabel(’Which is your mother tongue?’); $select->setValueOptions(array( ’0’ => ’French’, ’1’ => ’English’, ’2’ => ’Japanese’, ’3’ => ’Chinese’, )); 12 13 14 $form = new Form(’language’); $form->add($select); Using the array notation: 1 use ZendFormForm; 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $form = new Form(’my-form’); $form->add(array( ’type’ => ’ZendFormElementSelect’, ’name’ => ’language’, ’options’ => array( ’label’ => ’Which is your mother tongue?’, ’value_options’ => array( ’0’ => ’French’, ’1’ => ’English’, ’2’ => ’Japanese’, ’3’ => ’Chinese’, ), ) )); You can add an empty option (option with no value) using the "empty_option" option: 1 use ZendFormForm; 2 3 4 5 $form = new Form(’my-form’); $form->add(array( ’type’ => ’ZendFormElementSelect’, 119.3. Standard Elements 575
  • 614. Zend Framework 2 Documentation, Release 2.2.5 ’name’ => ’language’, ’options’ => array( ’label’ => ’Which is your mother tongue?’, ’empty_option’ => ’Please choose your language’, ’value_options’ => array( ’0’ => ’French’, ’1’ => ’English’, ’2’ => ’Japanese’, ’3’ => ’Chinese’, ), ) 6 7 8 9 10 11 12 13 14 15 16 )); 17 Option groups are also supported. You just need to add an ‘options’ key to the value options. use ZendFormElement; use ZendFormForm; 1 2 3 $select = new ElementSelect(’language’); $select->setLabel(’Which is your mother tongue?’); $select->setValueOptions(array( ’european’ => array( ’label’ => ’European languages’, ’options’ => array( ’0’ => ’French’, ’1’ => ’Italian’, ), ), ’asian’ => array( ’label’ => ’Asian languages’, ’options’ => array( ’2’ => ’Japanese’, ’3’ => ’Chinese’, ), ), )); 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 $form = new Form(’language’); $form->add($select); 23 24 Public Methods The following methods are in addition to the inherited methods of ZendFormElement . setOptions(array $options) Set options for an element of type Checkbox. Accepted options, in addition to the inherited options of ZendFormElementCheckbox , are: "value_options" and "empty_option", which call setValueOptions and setEmptyOption, respectively. setValueOptions(array $options) Set the value options for the select element. The array must contain key => value pairs. getValueOptions() Return the value options. Return type array setEmptyOption($emptyOption) 576 Chapter 119. Form Elements
  • 615. Zend Framework 2 Documentation, Release 2.2.5 Optionally set a label for an empty option (option with no value). It is set to “null” by default, which means that no empty option will be rendered. getEmptyOption() Get the label for the empty option (null if none). Return type string|null 119.3.14 Submit ZendFormElementSubmit represents a submit button form input. ZendFormViewHelperFormSubmit view helper. It can be used with the ZendFormElementSubmit extends from ZendFormElement. Basic Usage This element automatically adds a "type" attribute of value "submit". 1 2 use ZendFormElement; use ZendFormForm; 3 4 5 $submit = new ElementSubmit(’my-submit’); $submit->setValue(’Submit Form’); 6 7 8 $form = new Form(’my-form’); $form->add($submit); 119.3.15 Text ZendFormElementText represents a text ZendFormViewHelperFormText view helper. form input. It can be used with the ZendFormElementText extends from ZendFormElement. Basic Usage This element automatically adds a "type" attribute of value "text". 1 2 use ZendFormElement; use ZendFormForm; 3 4 5 $text = new ElementText(’my-text’); $text->setLabel(’Enter your name’); 6 7 8 $form = new Form(’my-form’); $form->add($text); 119.3.16 Textarea ZendFormElementTextarea represents a textarea form input. ZendFormViewHelperFormTextarea view helper. It can be used with the ZendFormElementTextarea extends from ZendFormElement. 119.3. Standard Elements 577
  • 616. Zend Framework 2 Documentation, Release 2.2.5 Basic Usage This element automatically adds a "type" attribute of value "textarea". 1 2 use ZendFormElement; use ZendFormForm; 3 4 5 $textarea = new ElementTextarea(’my-textarea’); $textarea->setLabel(’Enter a description’); 6 7 8 $form = new Form(’my-form’); $form->add($textarea); 119.4 HTML5 Elements 119.4.1 Color ZendFormElementColor is meant to be paired with the ZendFormViewHelperFormColor for HTML5 inputs with type color. This element adds filters and a Regex validator to it’s input filter specification in order to validate a HTML5 valid simple color value on the server. Basic Usage This element automatically adds a "type" attribute of value "color". 1 2 use ZendFormElement; use ZendFormForm; 3 4 5 $color = new ElementColor(’color’); $color->setLabel(’Background color’); 6 7 8 $form = new Form(’my-form’); $form->add($color); Here is the same example using the array notation: 1 use ZendFormForm; 2 3 4 5 6 7 8 9 10 $form = new Form(’my-form’); $form->add(array( ’type’ => ’ZendFormElementColor’, ’name’ => ’color’, ’options’ => array( ’label’ => ’Background color’ ) )); Public Methods The following methods are in addition to the inherited methods of ZendFormElement. getInputSpecification() Returns a input filter 578 specification, which includes ZendFilterStringTrim and Chapter 119. Form Elements
  • 617. Zend Framework 2 Documentation, Release 2.2.5 ZendFilterStringToLower filters, and a ZendValidatorRegex to validate the RGB hex format. Return type array 119.4.2 Date ZendFormElementDate is meant to be paired with the ZendFormViewHelperFormDate for HTML5 inputs with type date. This element adds filters and validators to it’s input filter specification in order to validate HTML5 date input values on the server. Basic Usage This element automatically adds a "type" attribute of value "date". 1 2 use ZendFormElement; use ZendFormForm; 3 4 5 6 7 8 9 10 11 12 13 14 $date = new ElementDate(’appointment-date’); $date ->setLabel(’Appointment Date’) ->setAttributes(array( ’min’ => ’2012-01-01’, ’max’ => ’2020-01-01’, ’step’ => ’1’, // days; default step interval is 1 day )) ->setOptions(array( ’format’ => ’Y-m-d’ )); 15 16 17 $form = new Form(’my-form’); $form->add($date); Here is with the array notation: 1 use ZendFormForm; 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $form = new Form(’my-form’); $form->add(array( ’type’ => ’ZendFormElementDate’, ’name’ => ’appointment-date’, ’options’ => array( ’label’ => ’Appointment Date’, ’format’ => ’Y-m-d’ ), ’attributes’ => array( ’min’ => ’2012-01-01’, ’max’ => ’2020-01-01’, ’step’ => ’1’, // days; default step interval is 1 day ) )); Note: Note: the min, max, and step attributes should be set prior to calling ZendForm::prepare(). Otherwise, the default input specification for the element may not contain the correct validation rules. 119.4. HTML5 Elements 579
  • 618. Zend Framework 2 Documentation, Release 2.2.5 Public Methods The following methods are in addition to the inherited methods of ZendFormElementDateTime. getInputSpecification() Returns a input filter specification, which includes ZendFilterStringTrim and will add the appropriate validators based on the values from the min, max, and step attributes and format option. See getInputSpecification in ZendFormElementDateTime for more information. One difference from ZendFormElementDateTime is that the ZendValidatorDateStep validator will expect the step attribute to use an interval of days (default is 1 day). Return type array 119.4.3 DateTime ZendFormElementDateTime is meant to be paired with the ZendFormViewHelperFormDateTime for HTML5 inputs with type datetime. This element adds filters and validators to it’s input filter specification in order to validate HTML5 datetime input values on the server. Basic Usage This element automatically adds a "type" attribute of value "datetime". 1 2 use ZendFormElement; use ZendFormForm; 3 4 5 6 7 8 9 10 11 12 13 14 $dateTime = new ElementDateTime(’appointment-date-time’); $dateTime ->setLabel(’Appointment Date/Time’) ->setAttributes(array( ’min’ => ’2010-01-01T00:00:00Z’, ’max’ => ’2020-01-01T00:00:00Z’, ’step’ => ’1’, // minutes; default step interval is 1 min )) ->setOptions(array( ’format’ => ’Y-m-dTH:iP’ )); 15 16 17 $form = new Form(’my-form’); $form->add($dateTime); Here is with the array notation: 1 use ZendFormForm; 2 3 4 5 6 7 8 9 10 11 12 $form = new Form(’my-form’); $form->add(array( ’type’ => ’ZendFormElementDateTime’, ’name’ => ’appointment-date-time’, ’options’ => array( ’label’ => ’Appointment Date/Time’, ’format’ => ’Y-m-dTH:iP’ ), ’attributes’ => array( ’min’ => ’2010-01-01T00:00:00Z’, 580 Chapter 119. Form Elements
  • 619. Zend Framework 2 Documentation, Release 2.2.5 ’max’ => ’2020-01-01T00:00:00Z’, ’step’ => ’1’, // minutes; default step interval is 1 min 13 14 ) 15 16 )); Note: Note: the min, max, and step attributes should be set prior to calling ZendForm::prepare(). Otherwise, the default input specification for the element may not contain the correct validation rules. Public Methods The following methods are in addition to the inherited methods of ZendFormElement. getInputSpecification() Returns a input filter specification, which includes ZendFilterStringTrim and will add the appropriate validators based on the values from the min, max, and step attributes and format option. If the min attribute is set, a ZendValidatorGreaterThan validator will be added to ensure the date value is greater than the minimum value. If the max attribute is set, a ZendValidatorLessThanValidator validator will be added to ensure the date value is less than the maximum value. If the step attribute is set to “any”, step validations will be skipped. Otherwise, a ZendValidatorDateStep validator will be added to ensure the date value is within a certain interval of minutes (default is 1 minute). The input filter specification also includes a ZendValidatorDate validator to ensure the format of the value. If the format option is set, that format will be used. Otherwise the default format will be used. Return type array setOptions(array $options) Set options for an element of type DateTime. The accepted option, in addition to the inherited options of ZendFormElement , is: "format", which calls setFormat. setFormat(string $format) Sets the format used to validate the value. Accepts a DateTime compatible string. getFormat() Return the DateTime format used to validate the value. Return type String 119.4.4 DateTimeLocal ZendFormElementDateTimeLocal is meant to be paired with the ZendFormViewHelperFormDateTimeLocal for HTML5 inputs with type datetime-local. This element adds filters and validators to it’s input filter specification in order to validate HTML5 a local datetime input values on the server. Basic Usage This element automatically adds a "type" attribute of value "datetime-local". 119.4. HTML5 Elements 581
  • 620. Zend Framework 2 Documentation, Release 2.2.5 1 2 use ZendFormElement; use ZendFormForm; 3 4 5 6 7 8 9 10 11 12 13 14 $dateTimeLocal = new ElementDateTimeLocal(’appointment-date-time’); $dateTimeLocal ->setLabel(’Appointment Date’) ->setAttributes(array( ’min’ => ’2010-01-01T00:00:00’, ’max’ => ’2020-01-01T00:00:00’, ’step’ => ’1’, // minutes; default step interval is 1 min )) ->setOptions(array( ’format’ => ’Y-m-dTH:i’ )); 15 16 17 $form = new Form(’my-form’); $form->add($dateTimeLocal); Here is with the array notation: 1 use ZendFormForm; 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $form = new Form(’my-form’); $form->add(array( ’type’ => ’ZendFormElementDateTimeLocal’, ’name’ => ’appointment-date-time’, ’options’ => array( ’label’ => ’Appointment Date’, ’format’ => ’Y-m-dTH:i’ ), ’attributes’ => array( ’min’ => ’2010-01-01T00:00:00’, ’max’ => ’2020-01-01T00:00:00’, ’step’ => ’1’, // minutes; default step interval is 1 min ) )); Note: Note: the min, max, and step attributes should be set prior to calling ZendForm::prepare(). Otherwise, the default input specification for the element may not contain the correct validation rules. Public Methods The following methods are in addition to the inherited methods of ZendFormElementDateTime. getInputSpecification() Returns a input filter specification, which includes ZendFilterStringTrim and will add the appropriate validators based on the values from the min, max, and step attributes and format option. See getInputSpecification in ZendFormElementDateTime for more information. Return type array 119.4.5 Email ZendFormElementEmail is meant to be paired with the ZendFormViewHelperFormEmail for HTML5 inputs with type email. This element adds filters and validators to it’s input filter specification in order to 582 Chapter 119. Form Elements
  • 621. Zend Framework 2 Documentation, Release 2.2.5 validate HTML5 valid email address on the server. Basic Usage This element automatically adds a "type" attribute of value "email". 1 2 use ZendFormElement; use ZendFormForm; 3 4 $form = new Form(’my-form’); 5 6 7 8 9 // Single email address $email = new ElementEmail(’email’); $email->setLabel(’Email Address’) $form->add($email); 10 11 12 13 14 15 16 // Comma separated list of emails $emails = new ElementEmail(’emails’); $emails ->setLabel(’Email Addresses’) ->setAttribute(’multiple’, true); $form->add($emails); Here is with the array notation: 1 use ZendFormForm; 2 3 4 5 6 7 8 9 10 $form = new Form(’my-form’); $form->add(array( ’type’ => ’ZendFormElementEmail’, ’name’ => ’email’, ’options’ => array( ’label’ => ’Email Address’ ), )); 11 12 13 14 15 16 17 18 19 20 21 $form->add(array( ’type’ => ’ZendFormElementEmail’, ’name’ => ’emails’, ’options’ => array( ’label’ => ’Email Addresses’ ), ’attributes’ => array( ’multiple’ => true ) )); Note: Note: the multiple attribute should be set prior to calling ZendForm::prepare(). Otherwise, the default input specification for the element may not contain the correct validation rules. Public Methods The following methods are in addition to the inherited methods of ZendFormElement. 119.4. HTML5 Elements 583
  • 622. Zend Framework 2 Documentation, Release 2.2.5 getInputSpecification() Returns a input filter specification, which includes a ZendFilterStringTrim filter, and a validator based on the multiple attribute. If the multiple attribute is unset or false, a ZendValidatorRegex validator will be added to validate a single email address. If the multiple attribute is true, a ZendValidatorExplode validator will be added to ensure the input string value is split by commas before validating each email address with ZendValidatorRegex. Return type array setValidator(ValidatorInterface $validator) Sets the primary validator to use for this element getValidator() Get the primary validator Return type ValidatorInterface setEmailValidator(ValidatorInterface $validator) Sets the email validator to use for multiple or single email addresses. getEmailValidator() Get the email validator to use for multiple or single email addresses. The default Regex validator in use is to match that of the browser validation, but you are free to set a different (more strict) email validator such as ZendValidatorEmail if you wish. 119.4.6 Month ZendFormElementMonth is meant to be paired with the ZendFormViewHelperFormMonth for HTML5 inputs with type month. This element adds filters and validators to it’s input filter specification in order to validate HTML5 month input values on the server. Basic Usage This element automatically adds a "type" attribute of value "month". 1 2 use ZendFormElement; use ZendFormForm; 3 4 5 6 7 8 9 10 11 $month = new ElementMonth(’month’); $month ->setLabel(’Month’) ->setAttributes(array( ’min’ => ’2012-01’, ’max’ => ’2020-01’, ’step’ => ’1’, // months; default step interval is 1 month )); 12 13 14 $form = new Form