SlideShare a Scribd company logo
Zend Framework 2 Documentation
Release 2.3.1dev
Zend Technologies Ltd.
March 22, 2014
zend framework 2
Contents
1 Overview 1
2 Installation 3
3 Getting Started with Zend Framework 2 5
3.1 Some assumptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
3.2 The tutorial application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
4 Getting started: A skeleton application 7
4.1 Using the Apache Web Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
4.2 Using the Built-in PHP CLI Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
4.3 Error reporting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
5 Routing and controllers 11
6 Create the controller 13
6.1 Initialise the view scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
7 Database and models 15
7.1 The database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
7.2 The model files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
7.3 Using ServiceManager to configure the table gateway and inject into the AlbumTable . . . . . . . . . 17
7.4 Back to the controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
7.5 Listing albums . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
8 Styling and Translations 21
9 Forms and actions 23
9.1 Adding new albums . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
9.2 Editing an album . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
9.3 Deleting an album . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
9.4 Ensuring that the home page displays the list of albums . . . . . . . . . . . . . . . . . . . . . . . . . 31
10 Conclusion 33
11 Getting Started with Zend Framework 2 35
11.1 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
11.2 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
i
12 A quick tour of the skeleton application 37
12.1 The dispatch cycle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
13 The MyTaskList application 39
13.1 The Checklist module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
13.2 The Module class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
14 The application’s pages 43
14.1 Routing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
14.2 The TaskController . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
14.3 The model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
14.4 Using Service Manager to configure the database credentials and inject into the controller . . . . . . 48
15 Listing tasks 51
15.1 Redirect the home page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
16 Styling 53
17 Adding new tasks 55
18 Editing a task 61
19 Deleting a task 65
20 Application Diagnostics 67
21 Step-by-step debugging 69
22 Conclusion 71
23 Zend Framework Tool (ZFTool) 73
23.1 Installation using Composer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
23.2 Manual installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
23.3 Without installation, using the PHAR file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
23.4 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
24 Learning Dependency Injection 77
24.1 Very brief introduction to Di. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
24.2 Simplest usage case (2 classes, one consumes the other) . . . . . . . . . . . . . . . . . . . . . . . . 77
24.3 Simplest Usage Case Without Type-hints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
24.4 Simplest usage case with Compiled Definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
24.5 Creating a precompiled definition for others to use . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
24.6 Using Multiple Definitions From Multiple Sources . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
24.7 Generating Service Locators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
25 Unit Testing a Zend Framework 2 application 87
25.1 Setting up the tests directory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
25.2 Bootstrapping your tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
25.3 Your first controller test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
25.4 A failing test case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
25.5 Configuring the service manager for the tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
25.6 Testing actions with POST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
25.7 Testing model entities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
25.8 Testing model tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
25.9 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
ii
26 Using the EventManager 101
26.1 Terminology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
26.2 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
26.3 Shared managers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
27 Wildcards 105
28 Listener aggregates 107
28.1 Introspecting results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
28.2 Short-circuiting listener execution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
28.3 Keeping it in order . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
28.4 Custom event objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
28.5 Putting it together: Implementing a simple caching system . . . . . . . . . . . . . . . . . . . . . . . 111
28.6 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
29 Advanced Configuration Tricks 115
29.1 System configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
29.2 Module Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
29.3 Configuration mapping table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
29.4 Configuration Priority . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
29.5 Manipulating merged configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
29.6 Configuration merging workflow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
30 Using ZendNavigation in your Album Module 123
30.1 Preparation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
30.2 Setting Up ZendNavigation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
30.3 Configuring our Site Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
30.4 Adding the Menu View Helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
30.5 Adding Breadcrumbs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
31 Using ZendPaginator in your Album Module 127
31.1 Preparation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
31.2 Modifying the AlbumTable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
31.3 Modifying the AlbumController . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
31.4 Updating the View Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
31.5 Creating the Pagination Control Partial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
32 Using the PaginationControl View Helper 135
33 Setting up a database adapter 137
33.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
33.2 Basic setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
33.3 Setting a static adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
34 Migration from Zend Framework 1 139
35 Namespacing Old Classes 141
35.1 Namespacing a ZF1 Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
35.2 HOWTO Namespace Your Models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
36 Running Zend Framework 2 and Zend Framework 1 in parallel 145
36.1 Use ZF2 in a ZF1 project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
36.2 Use ZF1 in a ZF2 project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
36.3 Run ZF1 and ZF2 together . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
iii
37 Introduction to ZendAuthentication 147
37.1 Adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
37.2 Results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
37.3 Identity Persistence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
37.4 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
38 Database Table Authentication 155
38.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
38.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
38.3 Advanced Usage: Persisting a DbTable Result Object . . . . . . . . . . . . . . . . . . . . . . . . . . 157
39 Digest Authentication 161
39.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
39.2 Specifics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
39.3 Identity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
40 HTTP Authentication Adapter 163
40.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
40.2 Design Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
40.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
40.4 Resolvers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
40.5 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
41 LDAP Authentication 167
41.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
41.2 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
41.3 The API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
41.4 Server Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
41.5 Collecting Debugging Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
41.6 Common Options for Specific Servers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
42 Authentication Validator 175
42.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
42.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
43 Introduction to ZendBarcode 177
44 Barcode creation using ZendBarcodeBarcode class 179
44.1 Using ZendBarcodeBarcode::factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
44.2 Drawing a barcode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
44.3 Rendering a barcode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
45 ZendBarcodeBarcode Objects 183
45.1 Common Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
45.2 Common Additional Getters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
45.3 Description of shipped barcodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
46 ZendBarcode Renderers 193
46.1 Common Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
46.2 ZendBarcodeRendererImage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
46.3 ZendBarcodeRendererPdf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
47 ZendCacheStorageAdapter 195
47.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
47.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
47.3 Basic Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
iv
47.4 The StorageInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
47.5 The AvailableSpaceCapableInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
47.6 The TotalSpaceCapableInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
47.7 The ClearByNamespaceInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
47.8 The ClearByPrefixInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
47.9 The ClearExpiredInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
47.10 The FlushableInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
47.11 The IterableInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
47.12 The OptimizableInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
47.13 The TaggableInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
47.14 The Apc Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
47.15 The Dba Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
47.16 The Filesystem Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
47.17 The Memcached Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
47.18 The Memory Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
47.19 The WinCache Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
47.20 The XCache Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
47.21 The ZendServerDisk Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
47.22 The ZendServerShm Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
47.23 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
48 ZendCacheStorageCapabilities 211
48.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
48.2 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
48.3 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
49 ZendCacheStoragePlugin 215
49.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
49.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
49.3 The ClearExpiredByFactor Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
49.4 The ExceptionHandler Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
49.5 The IgnoreUserAbort Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
49.6 The OptimizeByFactor Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
49.7 The Serializer Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
49.8 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
49.9 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
50 ZendCachePattern 219
50.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
50.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
50.3 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
51 ZendCachePatternCallbackCache 221
51.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
51.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
51.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
51.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
51.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
52 ZendCachePatternClassCache 223
52.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
52.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
52.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
52.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
52.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
v
53 ZendCachePatternObjectCache 227
53.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
53.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
53.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
53.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
53.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
54 ZendCachePatternOutputCache 231
54.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
54.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
54.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
54.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
54.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
55 ZendCachePatternCaptureCache 233
55.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
55.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
55.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
55.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
55.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
56 Introduction to ZendCaptcha 237
57 Captcha Operation 239
58 CAPTCHA Adapters 241
58.1 ZendCaptchaAbstractWord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
58.2 ZendCaptchaDumb . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
58.3 ZendCaptchaFiglet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
58.4 ZendCaptchaImage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
58.5 ZendCaptchaReCaptcha . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
59 Introduction 245
59.1 Theory of Operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
60 ZendCodeGenerator Reference 249
60.1 Abstract Classes and Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
60.2 Concrete CodeGenerator Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
61 ZendCodeGenerator Examples 257
62 Introduction to ZendConfig 265
62.1 Using ZendConfigConfig with a Reader Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
62.2 Using ZendConfigConfig with a PHP Configuration File . . . . . . . . . . . . . . . . . . . . . . . 266
63 Theory of Operation 267
64 ZendConfigReader 269
64.1 ZendConfigReaderIni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
64.2 ZendConfigReaderXml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
64.3 ZendConfigReaderJson . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
64.4 ZendConfigReaderYaml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
65 ZendConfigWriter 275
65.1 ZendConfigWriterIni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
65.2 ZendConfigWriterXml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
vi
65.3 ZendConfigWriterPhpArray . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277
65.4 ZendConfigWriterJson . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278
65.5 ZendConfigWriterYaml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278
66 ZendConfigProcessor 281
66.1 ZendConfigProcessorConstant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
66.2 ZendConfigProcessorFilter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
66.3 ZendConfigProcessorQueue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
66.4 ZendConfigProcessorToken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
66.5 ZendConfigProcessorTranslator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
67 The Factory 285
67.1 Loading configuration file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
67.2 Storing configuration file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
68 Introduction to ZendConsole 287
68.1 Writing console routes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
68.2 Handling console requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
68.3 Adding console usage info . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
69 Console routes and routing 293
69.1 Router configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
69.2 Basic route . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
69.3 Catchall route . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
69.4 Console routes cheat-sheet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
70 Console-aware modules 301
70.1 Application banner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
70.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
70.3 Best practices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
71 Console-aware action controllers 307
71.1 Handling console requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
71.2 Sending output to console . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309
71.3 Are we in a console? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309
71.4 Reading values from console parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311
72 Console adapters 315
72.1 Retrieving console adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315
72.2 Using console adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
73 Console prompts 319
73.1 Confirm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
73.2 Line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
73.3 Char . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
73.4 Select . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
74 Introduction 323
75 Declaring Getopt Rules 325
75.1 Declaring Options with the Short Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
75.2 Declaring Options with the Long Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
76 Fetching Options and Arguments 327
76.1 Handling Getopt Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327
76.2 Fetching Options by Name . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
vii
76.3 Reporting Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
76.4 Fetching Non-option Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
77 Configuring ZendConsoleGetopt 331
77.1 Adding Option Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
77.2 Adding Help Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
77.3 Adding Option Aliases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
77.4 Adding Argument Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
77.5 Adding Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
78 Introduction to ZendCrypt 335
79 Encrypt/decrypt using block ciphers 337
80 Key derivation function 339
80.1 Pbkdf2 adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
80.2 SaltedS2k adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
80.3 Scrypt adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
81 Password 343
81.1 Bcrypt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
81.2 Apache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
82 Public key cryptography 347
82.1 Diffie-Hellman . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347
82.2 RSA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348
83 ZendDbAdapter 351
83.1 Creating an Adapter - Quickstart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
83.2 Creating an Adapter Using Dependency Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
83.3 Query Preparation Through ZendDbAdapterAdapter::query() . . . . . . . . . . . . . . . . . . . . 352
83.4 Query Execution Through ZendDbAdapterAdapter::query() . . . . . . . . . . . . . . . . . . . . . 353
83.5 Creating Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353
83.6 Using the Driver Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353
83.7 Using The Platform Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
83.8 Using The Parameter Container . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356
83.9 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357
84 ZendDbResultSet 359
84.1 Quickstart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359
84.2 ZendDbResultSetResultSet and ZendDbResultSetAbstractResultSet . . . . . . . . . . . . . . . . 360
84.3 ZendDbResultSetHydratingResultSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360
85 ZendDbSql 363
85.1 ZendDbSqlSql (Quickstart) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
85.2 ZendDbSql’s Select, Insert, Update and Delete . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
85.3 ZendDbSqlSelect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
85.4 ZendDbSqlInsert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367
85.5 ZendDbSqlUpdate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
85.6 ZendDbSqlDelete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
85.7 ZendDbSqlWhere & ZendDbSqlHaving . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
86 ZendDbSqlDdl 375
86.1 Creating Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375
86.2 Altering Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376
86.3 Dropping Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376
viii
86.4 Executing DDL Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376
86.5 Currently Supported Data Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
86.6 Currently Supported Constraint Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
87 ZendDbTableGateway 379
87.1 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
87.2 TableGateway Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381
88 ZendDbRowGateway 383
88.1 Quickstart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383
88.2 ActiveRecord Style Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384
89 ZendDbMetadata 385
89.1 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
90 Dumping Variables 389
90.1 Example of dump() method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
91 Introduction to ZendDi 391
91.1 Dependency Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391
91.2 Dependency Injection Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391
92 ZendDi Quickstart 393
93 ZendDi Definition 397
93.1 DefinitionList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397
93.2 RuntimeDefinition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397
93.3 CompilerDefinition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398
93.4 ClassDefinition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399
94 ZendDi InstanceManager 401
94.1 Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401
94.2 Preferences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 402
94.3 Aliases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
95 ZendDi Configuration 405
96 ZendDi Debugging & Complex Use Cases 407
96.1 Debugging a DiC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407
96.2 Complex Use Cases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407
97 Introduction to ZendDom 411
98 ZendDomQuery 413
98.1 Theory of Operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
98.2 Methods Available . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414
99 Introduction to ZendEscaper 417
99.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417
99.2 What ZendEscaper is not . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417
100Theory of Operation 419
100.1 The Problem with Inconsistent Functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419
100.2 Why Contextual Escaping? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 420
101Configuring ZendEscaper 423
ix
102Escaping HTML 425
102.1 Examples of Bad HTML Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425
102.2 Examples of Good HTML Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426
103Escaping HTML Attributes 427
103.1 Examples of Bad HTML Attribute Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427
103.2 Examples of Good HTML Attribute Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 428
104Escaping Javascript 431
104.1 Examples of Bad Javascript Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431
104.2 Examples of Good Javascript Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 432
105Escaping Cascading Style Sheets 433
105.1 Examples of Bad CSS Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433
105.2 Examples of Good CSS Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433
106Escaping URLs 435
106.1 Examples of Bad URL Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435
106.2 Examples of Good URL Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435
107The EventManager 437
107.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 437
107.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 437
107.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440
107.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440
107.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 442
108Using Exceptions 447
108.1 Catching an Exception . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447
109Introduction to ZendFeed 449
109.1 Reading RSS Feed Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449
110Importing Feeds 451
110.1 Dumping the contents of a feed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451
111Retrieving Feeds from Web Pages 453
111.1 Find Feed Links . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 453
112Consuming an RSS Feed 455
112.1 Reading a feed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455
112.2 Get properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455
113Consuming an Atom Feed 457
113.1 Basic Use of an Atom Feed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457
114Consuming a Single Atom Entry 459
114.1 Reading a Single-Entry Atom Feed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459
115ZendFeed and Security 461
115.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461
115.2 Filtering data using HTMLPurifier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461
115.3 Escaping data using ZendEscaper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463
116ZendFeedReaderReader 465
116.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465
x
116.2 Importing Feeds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465
116.3 Retrieving Underlying Feed and Entry Sources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 466
116.4 Cache Support and Intelligent Requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467
116.5 Locating Feed URIs from Websites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 468
116.6 Attribute Collections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469
116.7 Retrieving Feed Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469
116.8 Retrieving Entry/Item Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 472
116.9 Extending Feed and Entry APIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 474
117ZendFeedWriterWriter 479
117.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479
117.2 Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479
117.3 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 480
117.4 Setting Feed Data Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 482
117.5 Setting Entry Data Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 484
118ZendFeedPubSubHubbub 487
118.1 What is PubSubHubbub? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 487
118.2 Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 487
118.3 ZendFeedPubSubHubbubPublisher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 488
118.4 ZendFeedPubSubHubbubSubscriber . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489
119ZendFileClassFileLocator 495
119.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495
119.2 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495
119.3 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495
120Introduction to ZendFilter 497
120.1 What is a filter? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497
120.2 Basic usage of filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497
120.3 Using the StaticFilter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498
120.4 Double filtering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498
121Standard Filter Classes 501
121.1 Alnum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501
121.2 Alpha . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502
121.3 BaseName . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502
121.4 Boolean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503
121.5 Callback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506
121.6 Compress and Decompress . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507
121.7 Digits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 512
121.8 Dir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 513
121.9 Encrypt and Decrypt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 513
121.10HtmlEntities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 519
121.11Int . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 521
121.12Null . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 521
121.13NumberFormat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 523
121.14PregReplace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 523
121.15RealPath . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 524
121.16StringToLower . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 525
121.17StringToUpper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 526
121.18StringTrim . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 526
121.19StripNewLines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 527
121.20StripTags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 528
121.21UriNormalize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 529
xi
122Word Filters 531
122.1 CamelCaseToDash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 531
122.2 CamelCaseToSeparator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 531
122.3 CamelCaseToUnderscore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 532
122.4 DashToCamelCase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 532
122.5 DashToSeparator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 533
122.6 DashToUnderscore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 533
122.7 SeparatorToCamelCase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 534
122.8 SeparatorToDash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 534
122.9 SeparatorToSeparator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 535
122.10UnderscoreToCamelCase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 536
122.11UnderscoreToSeparator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 536
122.12UnderscoreToDash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 537
123File Filter Classes 539
123.1 Decrypt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539
123.2 Encrypt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539
123.3 Lowercase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539
123.4 Rename . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539
123.5 RenameUpload . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 541
123.6 Uppercase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 543
124Filter Chains 545
124.1 Setting Filter Chain Order . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545
124.2 Using the Plugin Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545
125ZendFilterInflector 547
125.1 Transforming MixedCase and camelCaseText to another format . . . . . . . . . . . . . . . . . . . . 547
125.2 Static Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 549
125.3 Filter Inflector Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 550
125.4 Setting Many Rules At Once . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 550
125.5 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 552
126Using the StaticFilter 553
127Writing Filters 555
127.1 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 555
128Introduction 557
129Quick Start 559
129.1 Programmatic Form Creation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 559
129.2 Creation via Factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560
129.3 Factory-backed Form Extension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 564
129.4 Validating Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 565
129.5 Hinting to the Input Filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 566
129.6 Binding an object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 568
129.7 Rendering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 569
129.8 Validation Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 572
129.9 Using Annotations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 573
130Form Collections 577
130.1 Creating Fieldsets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 580
130.2 The Form Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 584
130.3 The Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 585
xii
130.4 The View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 585
130.5 Adding New Elements Dynamically . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 586
130.6 Validation groups for fieldsets and collection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 588
131File Uploading 591
131.1 Standard Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 591
131.2 File Post-Redirect-Get Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 594
131.3 HTML5 Multi-File Uploads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596
131.4 Upload Progress . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 597
131.5 Additional Info . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 601
132Advanced use of forms 603
132.1 Short names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 603
132.2 Creating custom elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 603
132.3 Handling dependencies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 607
132.4 The specific case of initializers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 609
133Form Elements 611
133.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 611
133.2 Element Base Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 611
133.3 Standard Elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 613
133.4 HTML5 Elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 629
134Form View Helpers 643
134.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 643
134.2 Standard Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 643
134.3 HTML5 Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 657
134.4 File Upload Progress Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661
135ZendHttp 663
135.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663
135.2 ZendHttp Request, Response and Headers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663
136The Request Class 665
136.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 665
136.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 665
136.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 666
136.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 666
136.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669
137The Response Class 671
137.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 671
137.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 671
137.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672
137.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672
137.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 674
138The Headers Class 677
138.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 677
138.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 677
138.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 678
138.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 678
138.5 ZendHttpHeaderHeaderInterface Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 680
138.6 ZendHttpHeaderAbstractAccept Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 680
138.7 ZendHttpHeaderAbstractDate Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 681
xiii
138.8 ZendHttpHeaderAbstractLocation Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 681
138.9 List of HTTP Header Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 682
138.10Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 687
139HTTP Client 689
139.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 689
139.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 689
139.3 Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 690
139.4 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 691
140HTTP Client - Connection Adapters 695
140.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 695
140.2 The Socket Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 695
140.3 The Proxy Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 698
140.4 The cURL Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 699
140.5 The Test Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 700
140.6 Creating your own connection adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 702
141HTTP Client - Advanced Usage 705
141.1 HTTP Redirections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705
141.2 Adding Cookies and Using Cookie Persistence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705
141.3 Setting Custom Request Headers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 707
141.4 File Uploads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 707
141.5 Sending Raw POST Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 708
141.6 HTTP Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 709
141.7 Sending Multiple Requests With the Same Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . 709
141.8 Data Streaming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 710
142HTTP Client - Static Usage 713
142.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 713
142.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 713
142.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 713
142.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 714
143Translating 715
143.1 Adding translations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 715
143.2 Supported formats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716
143.3 Setting a locale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716
143.4 Translating messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716
143.5 Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716
144I18n View Helpers 717
144.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 717
144.2 CurrencyFormat Helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 717
144.3 DateFormat Helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 719
144.4 NumberFormat Helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 720
144.5 Plural Helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 722
144.6 Translate Helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 723
144.7 TranslatePlural Helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 724
144.8 Abstract Translator Helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 725
145I18n Filters 727
145.1 Alnum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 727
145.2 Alpha . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 728
145.3 NumberFormat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 728
xiv
145.4 NumberParse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 729
146I18n Validators 731
147Float 733
147.1 Supported options for ZendI18nValidatorFloat . . . . . . . . . . . . . . . . . . . . . . . . . . . . 733
147.2 Simple float validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 733
147.3 Localized float validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 733
147.4 Int . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 734
148Introduction 735
149File Upload Input 739
149.1 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 739
150Introduction 741
151Basic Usage 743
151.1 Pretty-printing JSON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 743
152Advanced Usage 745
152.1 JSON Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 745
152.2 Encoding PHP objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 745
152.3 Internal Encoder/Decoder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 746
152.4 JSON Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 746
153XML to JSON conversion 747
153.1 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 747
154ZendJsonServer - JSON-RPC server 749
154.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 749
154.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 749
154.3 Advanced Details . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 751
155Introduction to ZendLdap 757
155.1 Theory of operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 757
156API overview 761
156.1 Configuration / options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 761
156.2 API Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 762
157ZendLdapLdap 763
157.1 ZendLdapCollection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 764
158ZendLdapAttribute 765
159ZendLdapConverterConverter 767
160ZendLdapDn 769
161ZendLdapFilter 773
162ZendLdapNode 777
163ZendLdapNodeRootDse 779
163.1 OpenLDAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 781
163.2 ActiveDirectory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 781
xv
163.3 eDirectory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 782
164ZendLdapNodeSchema 785
164.1 OpenLDAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 787
164.2 ActiveDirectory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 788
165ZendLdapLdifEncoder 789
166Usage Scenarios 791
166.1 Authentication scenarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791
166.2 Basic CRUD operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791
166.3 Extended operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 793
167Tools 795
167.1 Creation and modification of DN strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 795
167.2 Using the filter API to create search filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 795
167.3 Modify LDAP entries using the Attribute API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 795
168Object-oriented access to the LDAP tree using ZendLdapNode 797
168.1 Basic CRUD operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 797
168.2 Extended operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 797
168.3 Tree traversal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 797
169Getting information from the LDAP server 799
169.1 RootDSE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 799
169.2 Schema Browsing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 799
170Serializing LDAP data to and from LDIF 801
170.1 Serialize a LDAP entry to LDIF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 801
170.2 Deserialize a LDIF string into a LDAP entry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 802
171The AutoloaderFactory 805
171.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 805
171.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 805
171.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 806
171.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 806
171.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 806
172The StandardAutoloader 807
172.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 807
172.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 808
172.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 809
172.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 809
172.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 810
173The ClassMapAutoloader 811
173.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 811
173.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 811
173.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 812
173.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 812
173.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 813
174The ModuleAutoloader 815
174.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 815
174.2 Quickstart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 815
174.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 815
xvi
174.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 815
174.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 816
175The SplAutoloader Interface 817
175.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 817
175.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 817
175.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 818
175.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 818
175.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 819
176The PluginClassLoader 821
176.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821
176.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821
176.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 822
176.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 822
176.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 823
177The ShortNameLocator Interface 827
177.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 827
177.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 827
177.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 827
177.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 828
177.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 828
178The PluginClassLocator interface 829
178.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 829
178.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 829
178.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 829
178.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 829
178.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 830
179The Class Map Generator utility: bin/classmap_generator.php 831
179.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831
179.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831
179.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831
180ZendLog 833
180.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 833
180.2 Creating a Log . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 833
180.3 Logging Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 834
180.4 Destroying a Log . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 834
180.5 Using Built-in Priorities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 834
180.6 Understanding Log Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 835
180.7 Log PHP Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 835
181Writers 837
181.1 Writing to Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 837
181.2 Writing to Databases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 838
181.3 Writing to FirePHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 838
181.4 Stubbing Out the Writer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 839
181.5 Testing with the Mock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 839
181.6 Compositing Writers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 840
182Filters 841
182.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 841
xvii
182.2 Available filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 841
183Formatters 843
183.1 Simple Formatting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 843
183.2 Formatting to XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 843
183.3 Formatting to FirePhp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 844
184Introduction to ZendMail 845
184.1 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 845
184.2 Configuring the default sendmail transport . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 846
185ZendMailMessage 847
185.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 847
185.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 847
185.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 849
185.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 849
185.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 852
186ZendMailTransport 853
186.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 853
186.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 853
186.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 854
186.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 855
186.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 855
187ZendMailTransportSmtpOptions 857
187.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 857
187.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 857
187.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 859
187.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 859
187.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 860
188ZendMailTransportFileOptions 861
188.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 861
188.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 861
188.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 861
188.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 861
188.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 862
189Introduction to ZendMath 863
189.1 Random number generator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 863
189.2 Big integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 864
190Overview 867
190.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 867
190.2 Theory of Operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 868
191Memory Manager 869
191.1 Creating a Memory Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 869
191.2 Managing Memory Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 869
191.3 Memory Manager Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 870
192Memory Objects 873
192.1 Movable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 873
192.2 Locked . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 873
192.3 Memory container ‘value’ property . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 873
xviii
192.4 Memory container interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 874
193ZendMime 877
193.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 877
193.2 Static Methods and Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 877
193.3 Instantiating ZendMime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 878
194ZendMimeMessage 879
194.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 879
194.2 Instantiation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 879
194.3 Adding MIME Parts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 879
194.4 Boundary handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 879
194.5 Parsing a string to create a ZendMimeMessage object . . . . . . . . . . . . . . . . . . . . . . . . . 880
194.6 Available methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 880
195ZendMimePart 881
195.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 881
195.2 Instantiation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 881
195.3 Methods for rendering the message part to a string . . . . . . . . . . . . . . . . . . . . . . . . . . . 881
195.4 Available methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 882
196Introduction to the Module System 883
196.1 The autoload_*.php Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 884
197The Module Manager 885
197.1 Module Manager Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 885
197.2 Module Manager Listeners . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 886
198The Module Class 889
198.1 A Minimal Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 889
198.2 A Typical Module Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 889
198.3 The “loadModules.post” Event . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 890
198.4 The MVC “bootstrap” Event . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 891
199The Module Autoloader 893
199.1 Module Autoloader Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 893
199.2 Non-Standard / Explicit Module Paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 894
199.3 Packaging Modules with Phar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 895
200Best Practices when Creating Modules 897
200.1 Keep the init() and onBootstrap() methods lightweight . . . . . . . . . . . . . . . . . . . . 897
200.2 Do not perform writes within a module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 897
200.3 Utilize a vendor prefix for module names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 897
200.4 Utilize a module prefix for service names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 898
201Introduction to the MVC Layer 899
201.1 Basic Application Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 899
201.2 Basic Module Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 900
201.3 Bootstrapping an Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 902
201.4 Bootstrapping a Modular Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 904
201.5 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 905
202Quick Start 907
202.1 Install the Zend Skeleton Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 907
202.2 Create a New Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 908
202.3 Update the Module Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 908
xix
202.4 Create a Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 909
202.5 Create a View Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 910
202.6 Create a Route . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 910
202.7 Tell the Application About our Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 912
202.8 Test it Out! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 912
203Default Services 915
203.1 Theory of Operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 915
203.2 ServiceManager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 915
203.3 Abstract Factories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 920
203.4 Plugin Managers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 922
203.5 ViewManager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 923
203.6 Application Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 924
203.7 Default Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 925
204Routing 929
204.1 Router Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 931
204.2 HTTP Route Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 931
204.3 HTTP Routing Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 937
204.4 Console Route Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 940
205The MvcEvent 941
205.1 Order of events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 942
205.2 MvcEvent::EVENT_BOOTSTRAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 942
205.3 MvcEvent::EVENT_ROUTE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 943
205.4 MvcEvent::EVENT_DISPATCH . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 943
205.5 MvcEvent::EVENT_DISPATCH_ERROR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 945
205.6 MvcEvent::EVENT_RENDER . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 946
205.7 MvcEvent::EVENT_RENDER_ERROR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 947
205.8 MvcEvent::EVENT_FINISH . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 948
206The SendResponseEvent 949
206.1 Listeners . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 949
206.2 Triggerers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 949
207Available Controllers 951
207.1 Common Interfaces Used With Controllers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 951
207.2 The AbstractActionController . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 953
207.3 The AbstractRestfulController . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 954
208Controller Plugins 957
208.1 AcceptableViewModelSelector Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 957
208.2 FlashMessenger Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 958
208.3 Forward Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 960
208.4 Identity Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 961
208.5 Layout Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 962
208.6 Params Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 962
208.7 Post/Redirect/Get Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 963
208.8 File Post/Redirect/Get Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 963
208.9 Redirect Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 964
208.10Url Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 965
209Examples 967
209.1 Controllers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 967
209.2 Bootstrapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 968
xx
210Introduction to ZendNavigation 969
210.1 Pages and Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 969
210.2 View Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 969
211Quick Start 971
212Pages 973
212.1 Common page features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 973
212.2 ZendNavigationPageMvc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 975
212.3 ZendNavigationPageUri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 978
212.4 Creating custom page types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 978
212.5 Creating pages using the page factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 979
213Containers 983
213.1 Creating containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 983
213.2 Adding pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 989
213.3 Removing pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 990
213.4 Finding pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 991
213.5 Iterating containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 993
213.6 Other operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 993
214View Helpers 997
214.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 997
214.2 Translation of labels and titles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 998
214.3 Integration with ACL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 998
214.4 Navigation setup used in examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 999
215View Helper - Breadcrumbs 1005
215.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1005
215.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1005
215.3 Specifying indentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1006
215.4 Customize output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1006
215.5 Rendering using a partial view script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1006
216View Helper - Links 1009
216.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1009
216.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1011
217View Helper - Menu 1013
217.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1013
217.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1014
217.3 Calling renderMenu() directly . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1015
217.4 Rendering the deepest active menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1016
217.5 Rendering with maximum depth . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1016
217.6 Rendering with minimum depth . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1017
217.7 Rendering only the active branch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1018
217.8 Rendering only the active branch with minimum depth . . . . . . . . . . . . . . . . . . . . . . . . . 1019
217.9 Rendering only the active branch with maximum depth . . . . . . . . . . . . . . . . . . . . . . . . . 1019
217.10Rendering only the active branch with maximum depth and no parents . . . . . . . . . . . . . . . . . 1020
217.11Rendering a custom menu using a partial view script . . . . . . . . . . . . . . . . . . . . . . . . . . 1020
218View Helper - Sitemap 1023
218.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1023
218.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1024
218.3 Rendering using no ACL role . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1025
xxi
218.4 Rendering using a maximum depth . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1026
219View Helper - Navigation Proxy 1029
219.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1029
219.2 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1029
220Introduction to ZendPaginator 1031
221Usage 1033
221.1 Paginating data collections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1033
221.2 The DbSelect adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1034
221.3 Rendering pages with view scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1035
222Configuration 1041
223Advanced usage 1043
223.1 Custom data source adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1043
223.2 Custom scrolling styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1043
223.3 Caching features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1044
224Introduction to ZendPermissionsAcl 1047
224.1 Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1047
224.2 Roles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1048
224.3 Creating the Access Control List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1049
224.4 Registering Roles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1049
224.5 Defining Access Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1050
224.6 Querying an ACL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1051
225Refining Access Controls 1053
225.1 Precise Access Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1053
225.2 Removing Access Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1054
226Advanced Usage 1057
226.1 Storing ACL Data for Persistence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1057
226.2 Writing Conditional ACL Rules with Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1057
227Introduction to ZendPermissionsRbac 1059
227.1 Roles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1059
227.2 Permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1059
227.3 Dynamic Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1059
228Methods 1061
229Examples 1063
229.1 Roles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1063
229.2 Permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1064
229.3 Dynamic Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1064
230Progress Bars 1067
230.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1067
230.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1067
230.3 Persistent Progress . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1067
230.4 Standard Adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1068
231File Upload Handlers 1071
231.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1071
xxii
231.2 Methods of Reporting Progress . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1071
231.3 Standard Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1072
232Introduction to ZendSerializer 1075
232.1 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1075
232.2 Basic configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1076
232.3 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1076
233ZendSerializerAdapter 1079
233.1 The PhpSerialize Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1079
233.2 The IgBinary Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1079
233.3 The Wddx Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1079
233.4 The Json Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1080
233.5 The PythonPickle Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1080
233.6 The PhpCode Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1081
234Introduction to ZendServer 1083
235ZendServerReflection 1085
235.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1085
235.2 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1085
236ZendServiceManager 1087
237ZendServiceManager Quick Start 1091
237.1 Using Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1091
237.2 Modules as Service Providers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1092
237.3 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1092
238Delegator service factories 1097
238.1 Delegator factory signature . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1097
238.2 A Delegator factory use case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1097
239Lazy Services 1101
239.1 Use cases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1101
239.2 Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1101
239.3 Practical example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1101
239.4 Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1103
240Session Config 1105
240.1 Standard Config . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1105
240.2 Session Config . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1106
240.3 Custom Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1107
241Session Container 1109
241.1 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1109
241.2 Setting the Default Session Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1109
242Session Manager 1111
242.1 Initializing the Session Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1111
242.2 Session Compatibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1113
243Session Save Handlers 1115
243.1 Cache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1115
243.2 DbTableGateway . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1115
243.3 MongoDB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1116
xxiii
243.4 Custom Save Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1117
244Session Storage 1119
244.1 Array Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1119
244.2 Session Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1119
244.3 Session Array Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1120
244.4 Custom Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1120
245Session Validators 1121
245.1 Http User Agent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1121
245.2 Remote Addr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1121
245.3 Custom Validators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1122
246ZendSoapServer 1123
246.1 ZendSoapServer constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1123
246.2 Methods to define Web Service API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1124
246.3 Request and response objects handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1125
246.4 Document/Literal WSDL Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1127
247ZendSoapClient 1129
247.1 ZendSoapClient Constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1129
247.2 Performing SOAP Requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1130
248WSDL Accessor 1133
248.1 ZendSoapWsdl constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1133
248.2 addMessage() method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1133
248.3 addPortType() method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1134
248.4 addPortOperation() method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1134
248.5 addBinding() method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1134
248.6 addBindingOperation() method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1135
248.7 addSoapBinding() method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1135
248.8 addSoapOperation() method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1135
248.9 addService() method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1135
248.10Type mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1136
248.11addDocumentation() method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1137
248.12Get finalized WSDL document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1138
249AutoDiscovery 1139
249.1 AutoDiscovery Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1139
249.2 Class autodiscovering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1140
249.3 Functions autodiscovering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1141
249.4 Autodiscovering Datatypes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1141
249.5 WSDL Binding Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1141
250ZendStdlibHydrator 1143
250.1 HydratorInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1143
250.2 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1143
250.3 Available Implementations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1144
251ZendStdlibHydratorFilter 1145
251.1 Filter implementations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1145
251.2 Remove filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1146
251.3 Add filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1146
251.4 Use the composite for complex filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1147
251.5 Using the provider interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1148
xxiv
252ZendStdlibHydratorStrategy 1151
252.1 Adding strategies to the hydrators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1151
252.2 Available implementations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1152
252.3 Writing custom strategies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1152
253AggregateHydrator 1155
253.1 Installation requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1155
253.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1155
253.3 Advanced use cases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1156
254Introduction to ZendTag 1159
255Creating tag clouds with ZendTagCloud 1161
255.1 Decorators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1163
256Introduction to ZendTest 1167
257Unit testing with PHPUnit 1169
258Setup your TestCase 1171
259Testing your Controllers and MVC Applications 1173
260Assertions 1175
261Request Assertions 1177
262CSS Selector Assertions 1179
263XPath Assertions 1181
264Redirect Assertions 1183
265Response Header Assertions 1185
266ZendTextFiglet 1187
266.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1187
266.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1188
267ZendTextTable 1189
267.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1189
267.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1190
268ZendUri 1191
268.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1191
268.2 Creating a New URI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1191
268.3 Manipulating an Existing URI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1192
268.4 Common Instance Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1192
269Introduction to ZendValidator 1197
269.1 What is a validator? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1197
269.2 Basic usage of validators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1197
269.3 Customizing messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1198
269.4 Translating messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1199
270Standard Validation Classes 1201
270.1 Alnum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1201
xxv
271Alpha 1203
271.1 Supported options for ZendI18nValidatorAlpha . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1203
271.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1203
271.3 Using whitespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1203
271.4 Using different languages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1204
272Barcode 1205
272.1 Supported options for ZendValidatorBarcode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1207
272.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1208
272.3 Optional checksum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1208
272.4 Writing custom adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1208
273Between 1211
273.1 Supported options for ZendValidatorBetween . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1211
273.2 Default behaviour for ZendValidatorBetween . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1211
273.3 Validation exclusive the border values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1211
274Callback 1213
274.1 Supported options for ZendValidatorCallback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1213
274.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1213
274.3 Usage with closures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1213
274.4 Usage with class-based callbacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1214
274.5 Adding options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1215
275CreditCard 1217
275.1 Supported options for ZendValidatorCreditCard . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1218
275.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1218
275.3 Accepting defined credit cards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1218
275.4 Validation by using foreign APIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1219
275.5 Ccnum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1220
276Date 1221
276.1 Supported options for ZendValidatorDate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1221
276.2 Default date validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1221
276.3 Self defined date validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1221
277DbRecordExists and DbNoRecordExists 1223
277.1 Supported options for ZendValidatorDb* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1223
277.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1223
277.3 Excluding records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1224
277.4 Database Schemas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1225
277.5 Using a Select object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1225
278Digits 1227
278.1 Supported options for ZendValidatorDigits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1227
278.2 Validating digits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1227
279EmailAddress 1229
279.1 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1229
279.2 Options for validating Email Addresses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1229
279.3 Complex local parts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1230
279.4 Validating only the local part . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1230
279.5 Validating different types of hostnames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1230
279.6 Checking if the hostname actually accepts email . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1230
279.7 Validating International Domains Names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1231
xxvi
279.8 Validating Top Level Domains . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1232
279.9 Setting messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1232
280File Validation Classes 1233
280.1 Crc32 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1233
280.2 ExcludeExtension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1234
280.3 ExcludeMimeType . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1234
280.4 Exists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1234
280.5 Extension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1235
280.6 Hash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1236
280.7 ImageSize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1237
280.8 IsCompressed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1238
280.9 IsImage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1238
280.10Md5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1238
280.11MimeType . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1239
280.12NotExists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1240
280.13Sha1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1241
280.14Size . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1242
280.15UploadFile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1243
280.16WordCount . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1243
281Float 1245
281.1 Supported options for ZendI18nValidatorFloat . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1245
281.2 Simple float validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1245
281.3 Localized float validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1245
282GreaterThan 1247
282.1 Supported options for ZendValidatorGreaterThan . . . . . . . . . . . . . . . . . . . . . . . . . . . 1247
282.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1247
282.3 Validation inclusive the border value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1247
283Hex 1249
283.1 Supported options for ZendValidatorHex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1249
284Hostname 1251
284.1 Supported options for ZendValidatorHostname . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1251
284.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1251
284.3 Validating different types of hostnames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1251
284.4 Validating International Domains Names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1252
284.5 Validating Top Level Domains . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1253
285Iban 1255
285.1 Supported options for ZendValidatorIban . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1255
285.2 IBAN validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1255
286Identical 1257
286.1 Supported options for ZendValidatorIdentical . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1257
286.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1257
286.3 Identical objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1257
286.4 Form elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1258
286.5 Strict validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1260
286.6 Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1260
287InArray 1261
287.1 Supported options for ZendValidatorInArray . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1261
xxvii
287.2 Simple array validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1261
287.3 Array validation modes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1262
287.4 Recursive array validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1263
288Ip 1265
288.1 Supported options for ZendValidatorIp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1265
288.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1265
288.3 Validate IPv4 or IPV6 alone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1266
289Isbn 1267
289.1 Supported options for ZendValidatorIsbn . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1267
289.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1267
289.3 Setting an explicit ISBN validation type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1267
289.4 Specifying a separator restriction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1268
290LessThan 1269
290.1 Supported options for ZendValidatorLessThan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1269
290.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1269
290.3 Validation inclusive the border value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1269
291NotEmpty 1271
291.1 Supported options for ZendValidatorNotEmpty . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1271
291.2 Default behaviour for ZendValidatorNotEmpty . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1271
291.3 Changing behaviour for ZendValidatorNotEmpty . . . . . . . . . . . . . . . . . . . . . . . . . . . 1271
292PostCode 1273
292.1 Constructor options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1274
292.2 Supported options for ZendValidatorPostCode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1274
293Regex 1275
293.1 Supported options for ZendValidatorRegex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1275
293.2 Validation with ZendValidatorRegex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1275
293.3 Pattern handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1275
294Sitemap Validators 1277
294.1 SitemapChangefreq . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1277
294.2 SitemapLastmod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1277
294.3 SitemapLoc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1277
294.4 SitemapPriority . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1278
294.5 Supported options for ZendValidatorSitemap_* . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1278
295Step 1279
295.1 Supported options for ZendValidatorStep . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1279
295.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1279
295.3 Using floating-point values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1279
296StringLength 1281
296.1 Supported options for ZendValidatorStringLength . . . . . . . . . . . . . . . . . . . . . . . . . . . 1281
296.2 Default behaviour for ZendValidatorStringLength . . . . . . . . . . . . . . . . . . . . . . . . . . . 1281
296.3 Limiting the maximum allowed length of a string . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1281
296.4 Limiting the minimal required length of a string . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1282
296.5 Limiting a string on both sides . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1282
296.6 Encoding of values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1282
297File Validation Classes 1285
297.1 Crc32 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1285
xxviii
297.2 ExcludeExtension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1286
297.3 ExcludeMimeType . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1286
297.4 Exists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1286
297.5 Extension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1287
297.6 Hash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1288
297.7 ImageSize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1289
297.8 IsCompressed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1290
297.9 IsImage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1290
297.10Md5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1290
297.11MimeType . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1291
297.12NotExists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1292
297.13Sha1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1293
297.14Size . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1294
297.15UploadFile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1295
297.16WordCount . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1295
298Validator Chains 1297
298.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1297
299Writing Validators 1299
299.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1299
299.2 Creating a Simple Validation Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1299
299.3 Writing a Validation Class having Dependent Conditions . . . . . . . . . . . . . . . . . . . . . . . . 1300
299.4 Validation with Independent Conditions, Multiple Reasons for Failure . . . . . . . . . . . . . . . . . 1301
300Validation Messages 1303
300.1 Using pre-translated validation messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1303
300.2 Limit the size of a validation message . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1304
301Getting the Zend Framework Version 1305
301.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1305
301.2 Example of the compareVersion() Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1305
301.3 Example of the getLatest() Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1305
302ZendView Quick Start 1307
302.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1307
302.2 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1307
303The PhpRenderer 1321
303.1 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1321
303.2 Options and Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1325
303.3 Additional Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1325
304PhpRenderer View Scripts 1327
304.1 Escaping Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1328
305The ViewEvent 1329
305.1 Order of events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1329
305.2 ViewEvent::EVENT_RENDERER . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1330
305.3 ViewEvent::EVENT_RENDERER_POST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1331
305.4 ViewEvent::EVENT_RESPONSE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1331
306View Helpers 1333
306.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1333
306.2 Included Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1334
xxix
307View Helper - BasePath 1335
307.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1335
307.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1335
308View Helper - Cycle 1337
308.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1337
308.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1337
308.3 Working with two or more cycles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1338
309View Helper - Doctype 1339
309.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1339
309.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1339
309.3 Retrieving the Doctype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1340
309.4 Choosing a Doctype to Use with the Open Graph Protocol . . . . . . . . . . . . . . . . . . . . . . . 1340
309.5 Zend MVC View Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1341
310FlashMessenger Helper 1343
310.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1343
310.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1343
310.3 CSS Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1343
310.4 HTML Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1344
310.5 Sample Modification for Twitter Bootstrap 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1344
310.6 Alternative Configuration of the ViewHelper Layout . . . . . . . . . . . . . . . . . . . . . . . . . . 1345
311Gravatar Helper 1347
311.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1347
311.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1347
311.3 Custom Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1347
312View Helper - HeadLink 1349
312.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1349
312.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1349
313View Helper - HeadMeta 1351
313.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1351
313.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1352
313.3 Usage with XHTML1_RDFA doctype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1352
314View Helper - HeadScript 1355
314.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1355
314.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1357
314.3 Capturing Scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1357
315View Helper - HeadStyle 1359
315.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1359
315.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1362
315.3 Capturing Style Declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1362
316View Helper - HeadTitle 1363
316.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1363
316.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1363
317View Helper - HtmlList 1365
317.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1365
317.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1365
xxx
318View Helper - HTML Object 1369
318.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1369
318.2 Flash helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1369
318.3 Customizing the object by passing additional arguments . . . . . . . . . . . . . . . . . . . . . . . . 1369
319View Helper - Identity 1371
319.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1371
319.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1371
319.3 Using with ServiceManager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1371
320View Helper - InlineScript 1373
320.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1373
320.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1373
320.3 Capturing Scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1374
321View Helper - JSON 1375
321.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1375
321.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1375
322View Helper - Partial 1377
322.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1377
322.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1377
322.3 Using PartialLoop to Render Iterable Models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1378
323View Helper - Placeholder 1381
323.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1381
323.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1381
323.3 Aggregate Content . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1381
323.4 Capture Content . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1382
323.5 Concrete Implementations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1383
324View Helper - URL 1385
324.1 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1385
325Advanced usage of helpers 1387
325.1 Registering Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1387
325.2 Writing Custom Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1388
325.3 Registering Concrete Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1390
326Introduction to ZendXmlRpc 1391
326.1 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1391
327ZendXmlRpcClient 1393
327.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1393
327.2 Method Calls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1393
327.3 Types and Conversions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1394
327.4 Server Proxy Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1395
327.5 Error Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1396
327.6 Server Introspection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1397
327.7 From Request to Response . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1398
327.8 HTTP Client and Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1398
328ZendXmlRpcServer 1399
328.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1399
328.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1399
328.3 Server Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1399
xxxi
328.4 Anatomy of a webservice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1400
328.5 Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1400
328.6 Utilizing Namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1401
328.7 Custom Request Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1401
328.8 Custom Responses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1402
328.9 Handling Exceptions via Faults . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1402
328.10Caching Server Definitions Between Requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1402
328.11Usage Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1403
328.12Performance optimization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1407
329ZendServiceAkismet 1409
329.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1409
329.2 Verify an API key . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1409
329.3 Check for spam . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1410
329.4 Submitting known spam . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1410
329.5 Submitting false positives (ham) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1411
329.6 Zend-specific Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1411
330ZendServiceAmazon 1413
330.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1413
330.2 Country Codes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1414
330.3 Looking up a Specific Amazon Item by ASIN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1414
330.4 Performing Amazon Item Searches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1415
330.5 Using the Alternative Query API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1415
330.6 ZendServiceAmazon Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1416
331ZendServiceAmazonS3 1421
331.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1421
331.2 Registering with Amazon S3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1421
331.3 API Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1421
331.4 Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1421
331.5 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1422
331.6 Bucket operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1422
331.7 Object operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1423
331.8 Data Streaming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1424
331.9 Stream wrapper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1425
332ZendServiceAmazonSqs 1427
332.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1427
332.2 Registering with Amazon SQS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1427
332.3 API Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1427
332.4 Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1427
332.5 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1428
332.6 Queue operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1428
332.7 Message operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1429
333ZendServiceAmazonEc2 1431
333.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1431
333.2 What is Amazon Ec2? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1431
333.3 Static Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1431
334ZendServiceAmazonEc2: CloudWatch Monitoring 1433
334.1 CloudWatch Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1433
335ZendServiceAmazonEc2: Elastic Block Storage (EBS) 1435
xxxii
335.1 Create EBS Volumes and Snapshots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1435
335.2 Describing EBS Volumes and Snapshots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1436
335.3 Attach and Detaching Volumes from Instances . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1437
335.4 Deleting EBS Volumes and Snapshots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1437
336ZendServiceAmazonEc2: Elastic IP Addresses 1439
336.1 Allocating a new Elastic IP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1439
337ZendServiceAmazonEc2: Instances 1441
337.1 Instance Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1441
337.2 Running Amazon EC2 Instances . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1442
337.3 Amazon Instance Utilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1443
338ZendServiceAmazonEc2: Keypairs 1445
338.1 Creating a new Amazon Keypair . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1445
338.2 Deleting an Amazon Keypair . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1445
338.3 Describe an Amazon Keypair . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1445
339ZendServiceAmazonEc2: Regions and Availability Zones 1447
339.1 Amazon EC2 Regions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1447
339.2 Amazon EC2 Availability Zones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1447
340ZendServiceAmazonEc2: Reserved Instances 1449
340.1 How Reserved Instances are Applied . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1449
340.2 Reserved Instances Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1449
341ZendServiceAmazonEc2: Security Groups 1451
341.1 Security Group Maintenance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1451
341.2 Authorizing Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1452
341.3 Revoking Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1453
342ZendServiceAmazonEc2: Windows Instances 1455
342.1 Windows Instances Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1456
343ZendServiceAppleApns 1457
343.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1457
343.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1457
343.3 Feedback Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1459
344ZendServiceAudioscrobbler 1461
344.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1461
344.2 Users . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1461
344.3 Artists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1463
344.4 Tracks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1463
344.5 Tags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1464
344.6 Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1464
344.7 Forums . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1464
345ZendServiceDelicious 1467
345.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1467
345.2 Retrieving posts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1467
345.3 ZendServiceDeliciousPostList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1468
345.4 Editing posts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1469
345.5 Deleting posts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1469
345.6 Adding new posts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1470
345.7 Tags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1470
xxxiii
345.8 Bundles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1471
345.9 Public data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1471
345.10HTTP client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1472
346ZendServiceDeveloperGarden 1473
346.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1473
346.2 BaseUserService . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1474
346.3 IP Location . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1475
346.4 Local Search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1476
346.5 Send SMS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1476
346.6 SMS Validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1477
346.7 Voice Call . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1477
346.8 ConferenceCall . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1478
346.9 Performance and Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1480
347ZendServiceFlickr 1481
347.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1481
347.2 Finding Flickr Users’ Photos and Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1481
347.3 Finding photos From a Group Pool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1482
347.4 Retrieving Flickr Image Details . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1482
347.5 ZendServiceFlickr Result Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1482
348ZendServiceGoogleGcm 1485
348.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1485
348.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1485
349ZendServiceLiveDocx 1487
349.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1487
349.2 ZendServiceLiveDocxMailMerge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1489
350ZendServiceRackspace 1501
350.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1501
350.2 Registering with Rackspace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1501
350.3 Cloud Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1501
350.4 Cloud Servers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1502
350.5 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1502
351ZendServiceRackspaceServers 1505
351.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1505
351.2 Terminology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1505
351.3 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1506
351.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1507
351.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1511
352ZendServiceRackspaceFiles 1513
352.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1513
352.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1513
352.3 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1515
352.4 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1518
353ZendServiceReCaptcha 1521
353.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1521
353.2 Simplest use . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1521
353.3 Hiding email addresses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1522
xxxiv
354ZendServiceSlideShare 1525
354.1 Getting Started with ZendServiceSlideShare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1525
354.2 The SlideShow object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1525
354.3 Retrieving a single slide show . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1528
354.4 Retrieving Groups of Slide Shows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1528
354.5 ZendServiceSlideShare Caching policies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1529
354.6 Changing the behavior of the HTTP Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1529
355ZendServiceStrikeIron 1531
355.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1531
355.2 Registering with StrikeIron . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1532
355.3 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1532
355.4 Making Your First Query . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1532
355.5 Examining Results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1533
355.6 Handling Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1534
355.7 Checking Your Subscription . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1534
356ZendServiceStrikeIron: Bundled Services 1537
356.1 ZIP Code Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1537
356.2 U.S. Address Verification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1538
356.3 Sales & Use Tax Basic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1538
357ZendServiceStrikeIron: Advanced Uses 1541
357.1 Using Services by WSDL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1541
357.2 Viewing SOAP Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1541
358ZendServiceTechnorati 1543
358.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1543
358.2 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1543
358.3 Making Your First Query . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1543
358.4 Consuming Results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1544
358.5 Handling Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1546
358.6 Checking Your API Key Daily Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1546
358.7 Available Technorati Queries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1546
358.8 ZendServiceTechnorati Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1550
359ZendServiceTwitter 1555
359.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1555
359.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1555
359.3 Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1557
359.4 Account Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1558
359.5 Application Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1558
359.6 Blocking Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1559
359.7 Direct Message Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1559
359.8 Favorites Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1560
359.9 Friendship Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1561
359.10Search Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1561
359.11Status Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1562
359.12User Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1564
360ZendServiceWindowsAzure 1565
360.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1565
360.2 Installing the Windows Azure SDK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1565
360.3 API Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1565
360.4 Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1565
xxxv
360.5 Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1566
361ZendServiceWindowsAzureStorageBlob 1567
361.1 API Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1567
361.2 Root container . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1569
361.3 Blob storage stream wrapper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1569
361.4 Shared Access Signature . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1570
362ZendServiceWindowsAzureStorageTable 1573
362.1 Operations on tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1573
362.2 Operations on entities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1574
362.3 Table storage session handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1580
363ZendServiceWindowsAzureStorageQueue 1583
363.1 API Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1583
364Copyright Information 1587
365Introduction to Zend Framework 2 1589
366User Guide 1591
367Getting Started With Zend Studio 10 & Zend Server 6 1593
368Zend Framework Tool (ZFTool) 1595
369Learning Zend Framework 2 1597
370Migration 1599
371Zend Framework 2 Reference 1601
371.1 ZendAuthentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1601
371.2 ZendBarcode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1601
371.3 ZendCache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1601
371.4 ZendCaptcha . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1602
371.5 ZendCodeGenerator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1602
371.6 ZendConfig . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1602
371.7 ZendConsole . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1602
371.8 ZendConsoleGetopt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1602
371.9 ZendCrypt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1603
371.10ZendDb . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1603
371.11ZendDebug . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1603
371.12ZendDi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1603
371.13ZendDom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1603
371.14ZendEscaper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1604
371.15ZendEventManager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1604
371.16ZendException . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1604
371.17ZendFeed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1604
371.18ZendFile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1604
371.19ZendFilter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1605
371.20ZendForm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1605
371.21ZendHttp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1605
371.22ZendI18n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1605
371.23ZendInputFilter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1606
371.24ZendJson . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1606
371.25ZendLdap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1606
xxxvi
371.26ZendLoader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1606
371.27ZendLog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1607
371.28ZendMail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1607
371.29ZendMath . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1607
371.30ZendMemory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1607
371.31ZendMime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1607
371.32ZendModuleManager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1607
371.33ZendMvc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1608
371.34ZendNavigation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1608
371.35ZendPaginator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1608
371.36ZendPermissionsAcl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1608
371.37ZendPermissionsRbac . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1609
371.38ZendProgressBar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1609
371.39ZendSerializer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1609
371.40ZendServer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1609
371.41ZendServiceManager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1609
371.42ZendSession . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1609
371.43ZendSoap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1610
371.44ZendStdlib . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1610
371.45ZendTag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1610
371.46ZendTest . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1610
371.47ZendText . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1610
371.48ZendUri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1610
371.49ZendValidator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1610
371.50ZendVersion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1611
371.51ZendView . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1611
371.52ZendXmlRpc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1612
372Services for Zend Framework 2 Reference 1613
372.1 ZendServiceAkismet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1613
372.2 ZendServiceAmazon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1613
372.3 ZendServiceAppleApns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1613
372.4 ZendServiceAudioscrobbler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1613
372.5 ZendServiceDelicious . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1614
372.6 ZendServiceDeveloperGarden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1614
372.7 ZendServiceFlickr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1614
372.8 ZendServiceGoogleGcm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1614
372.9 ZendServiceLiveDocx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1614
372.10ZendServiceRackspace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1614
372.11ZendServiceReCaptcha . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1614
372.12ZendServiceSlideShare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1614
372.13ZendServiceStrikeIron . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1615
372.14ZendServiceTechnorati . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1615
372.15ZendServiceTwitter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1615
372.16ZendServiceWindowsAzure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1615
373Copyright 1617
374Indices and tables 1619
xxxvii
xxxviii
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 exten-
sible 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, vali-
dation, 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.3.1dev
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 en-
vironment 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.3.1dev
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.23 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 distribution’s 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.
Note: Alternatively, if you are using PHP 5.4+ you may use the built-in web server instead of Apache for development.
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 Description
List of
albums
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.
Add new
album
This page will provide a form for adding a new album.
Edit album This page will provide a form for editing an album.
Delete
album
This page will confirm that we want to delete an album and then delete it.
5
Zend Framework 2 Documentation, Release 2.3.1dev
We will also need to store our data into a database. We will only need one table with these fields in it:
Field name Type Null? Notes
id integer No Primary key, auto-increment
artist varchar(100) No
title varchar(100) No
6 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 php composer.phar create-project --repository-url="https://packages.zendframework.com" zendframework
2 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 php composer.phar self-update
2 php composer.phar install
3 php composer.phar update
from the zf2-tutorial folder. This takes a while. You should see an output like:
1 Installing dependencies from lock file
2 - Installing zendframework/zendframework (dev-master)
3 Cloning 18c8e223f070deb07c17543ed938b54542aa0ed8
4
5 Generating autoload files
Note: If you see this message:
1 [RuntimeException]
2 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:
1 php composer.phar install
2 php composer.phar update
run instead:
7
Zend Framework 2 Documentation, Release 2.3.1dev
1 COMPOSER_PROCESS_TIMEOUT=5000 php composer.phar install
2 COMPOSER_PROCESS_TIMEOUT=5000 php composer.phar update
Note: For windows users with wamp:
1. Install composer for windows Check composer is properly installed by running
1 composer
2. Install git for windows. Also need to add git path in windows environment variable Check git is properly
installed by running
1 git
3. Now install zf2 using command
1 composer create-project --repository-url="https://packages.zendframework.com" -s dev zendframewo
We can now move on to the web server setup.
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://zf2-tutorial.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 <VirtualHost *:80>
2 ServerName zf2-tutorial.localhost
3 DocumentRoot /path/to/zf2-tutorial/public
4 SetEnv APPLICATION_ENV "development"
5 <Directory /path/to/zf2-tutorial/public>
6 DirectoryIndex index.php
7 AllowOverride All
8 Order allow,deny
9 Allow from all
10 </Directory>
11 </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://zf2-tutorial.localhost.
1 127.0.0.1 zf2-tutorial.localhost localhost
Restart Apache.
If you’ve done it correctly, it should look something like this:
8 Chapter 4. Getting started: A skeleton application
Zend Framework 2 Documentation, Release 2.3.1dev
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 RewriteCond %{REQUEST_FILENAME} !-f
2 RewriteRule ^ index.php [NC,L]
You now have a working skeleton application and we can start adding the specifics for our application.
4.2 Using the Built-in PHP CLI Server
Alternatively — if you are using PHP 5.4 or above — you can use the built-in CLI server (cli-server). To do this, you
just start the server in the root directory:
1 php -S 0.0.0.0:8080 -t public/ public/index.php
This will make the website available on port 8080 on all network interfaces, using public/index.php to handle
routing. This means the site is accessible via http://localhost:8080 or http://<your-local-IP>:8080.
If you’ve done it right, you should see the same result as with Apache above.
To test that your routing is working, navigate to http://localhost:8080/1234 and you should see the same error page as
with Apache above.
Note: The built-in CLI server is for development only.
4.3 Error reporting
Optionally, when using Apache, you can use the APPLICATION_ENV setting in your VirtualHost to let PHP
output all its errors to the browser. This can be useful during the development of your application.
Edit index.php from the zf2-tutorial/public/ directory and change it to the following:
1 <?php
2
3 /**
4 * Display all errors when APPLICATION_ENV is development.
5 */
6 if ($_SERVER[’APPLICATION_ENV’] == ’development’) {
7 error_reporting(E_ALL);
8 ini_set("display_errors", 1);
9 }
10
11 /**
12 * This makes our life easier when dealing with paths. Everything is relative
13 * to the application root now.
14 */
15 chdir(dirname(__DIR__));
16
17 // Decline static file requests back to the PHP built-in webserver
18 if (php_sapi_name() === ’cli-server’ && is_file(__DIR__ . parse_url($_SERVER[’REQUEST_URI’], PHP_URL
19 return false;
20 }
4.2. Using the Built-in PHP CLI Server 9
Zend Framework 2 Documentation, Release 2.3.1dev
21
22 // Setup autoloading
23 require ’init_autoloader.php’;
24
25 // Run the application!
26 ZendMvcApplication::init(require ’config/application.config.php’)->run();
10 Chapter 4. Getting started: A skeleton application
CHAPTER 5
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 Description
Home 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.
Add new
album
This page will provide a form for adding a new album.
Edit album This page will provide a form for editing an album.
Delete
album
This page will confirm that we want to delete an album and then delete it.
Before we set up our files, it’s 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 Controller Action
Home AlbumController index
Add new album AlbumController add
Edit album AlbumController edit
Delete album AlbumController delete
The mapping of a URL to a particular action is done using routes that are defined in the module’s
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 return array(
2 ’controllers’ => array(
3 ’invokables’ => array(
4 ’AlbumControllerAlbum’ => ’AlbumControllerAlbumController’,
5 ),
6 ),
7
8 // The following section is new and should be added to your file
9 ’router’ => array(
10 ’routes’ => array(
11 ’album’ => array(
12 ’type’ => ’segment’,
13 ’options’ => array(
11
Zend Framework 2 Documentation, Release 2.3.1dev
14 ’route’ => ’/album[/][:action][/:id]’,
15 ’constraints’ => array(
16 ’action’ => ’[a-zA-Z][a-zA-Z0-9_-]*’,
17 ’id’ => ’[0-9]+’,
18 ),
19 ’defaults’ => array(
20 ’controller’ => ’AlbumControllerAlbum’,
21 ’action’ => ’index’,
22 ),
23 ),
24 ),
25 ),
26 ),
27
28 ’view_manager’ => array(
29 ’template_path_stack’ => array(
30 ’album’ => __DIR__ . ’/../view’,
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 Page Action
/album Home (list of albums) index
/album/add Add new album add
/album/edit/2 Edit album with an id of 2 edit
/album/delete/4 Delete album with an id of 4 delete
12 Chapter 5. Routing and controllers
CHAPTER 6
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 doesn’t 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. We’ll be using the stan-
dard AbstractActionController, but if you’re intending to write a RESTful web service,
AbstractRestfulController may be useful.
Let’s go ahead and create our controller class AlbumController.php at
zf2-tutorials/module/Album/src/Album/Controller :
1 namespace AlbumController;
2
3 use ZendMvcControllerAbstractActionController;
4 use ZendViewModelViewModel;
5
6 class AlbumController extends AbstractActionController
7 {
8 public function indexAction()
9 {
10 }
11
12 public function addAction()
13 {
14 }
15
16 public function editAction()
17 {
18 }
19
20 public function deleteAction()
21 {
22 }
23 }
13
Zend Framework 2 Documentation, Release 2.3.1dev
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 won’t work yet until we set up the views. The URLs
for each action are:
URL Method called
http://zf2-tutorial.localhost/album AlbumControllerAlbumController::indexAction
http://zf2-tutorial.localhost/album/addAlbumControllerAlbumController::addAction
http://zf2-tutorial.localhost/album/editAlbumControllerAlbumController::editAction
http://zf2-tutorial.localhost/album/deleteAlbumControllerAlbumController::deleteAction
We now have a working router and the actions are set up for each page of our application.
It’s time to build the view and the model layer.
6.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 module’s 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.
14 Chapter 6. Create the controller
CHAPTER 7
Database and models
7.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 application’s 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 PHP’s PDO driver, so create a database called zf2tutorial, and run these SQL
statements to create the album table with some data in it.
1 CREATE TABLE album (
2 id int(11) NOT NULL auto_increment,
3 artist varchar(100) NOT NULL,
4 title varchar(100) NOT NULL,
5 PRIMARY KEY (id)
6 );
7 INSERT INTO album (artist, title)
8 VALUES (’The Military Wives’, ’In My Dreams’);
9 INSERT INTO album (artist, title)
10 VALUES (’Adele’, ’21’);
11 INSERT INTO album (artist, title)
12 VALUES (’Bruce Springsteen’, ’Wrecking Ball (Deluxe)’);
13 INSERT INTO album (artist, title)
14 VALUES (’Lana Del Rey’, ’Born To Die’);
15 INSERT INTO album (artist, title)
16 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.
7.2 The model files
Zend Framework does not provide a ZendModel component because the model is your business logic and it’s 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
that load and save entities to the database. Another is to use an Object-relational mapping (ORM) technology, such as
Doctrine or Propel.
15
Zend Framework 2 Documentation, Release 2.3.1dev
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 sys-
tems. There is also a temptation to put database access code into controller action methods as these are exposed
by ZendDbTableGatewayAbstractTableGateway. Don’t do this!
Let’s start by creating a file called Album.php under module/Album/src/Album/Model:
1 namespace AlbumModel;
2
3 class Album
4 {
5 public $id;
6 public $artist;
7 public $title;
8
9 public function exchangeArray($data)
10 {
11 $this->id = (!empty($data[’id’])) ? $data[’id’] : null;
12 $this->artist = (!empty($data[’artist’])) ? $data[’artist’] : null;
13 $this->title = (!empty($data[’title’])) ? $data[’title’] : null;
14 }
15 }
Our Album entity object is a simple PHP class. In order to work with ZendDb’s TableGateway class, we need
to implement the exchangeArray() method. This method simply copies the data from the passed in array to our
entity’s 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 class AlbumTable
6 {
7 protected $tableGateway;
8
9 public function __construct(TableGateway $tableGateway)
10 {
11 $this->tableGateway = $tableGateway;
12 }
13
14 public function fetchAll()
15 {
16 $resultSet = $this->tableGateway->select();
17 return $resultSet;
18 }
19
20 public function getAlbum($id)
21 {
22 $id = (int) $id;
23 $rowset = $this->tableGateway->select(array(’id’ => $id));
24 $row = $rowset->current();
25 if (!$row) {
26 throw new Exception("Could not find row $id");
27 }
28 return $row;
16 Chapter 7. Database and models
Zend Framework 2 Documentation, Release 2.3.1dev
29 }
30
31 public function saveAlbum(Album $album)
32 {
33 $data = array(
34 ’artist’ => $album->artist,
35 ’title’ => $album->title,
36 );
37
38 $id = (int) $album->id;
39 if ($id == 0) {
40 $this->tableGateway->insert($data);
41 } else {
42 if ($this->getAlbum($id)) {
43 $this->tableGateway->update($data, array(’id’ => $id));
44 } else {
45 throw new Exception(’Album id does not exist’);
46 }
47 }
48 }
49
50 public function deleteAlbum($id)
51 {
52 $this->tableGateway->delete(array(’id’ => (int) $id));
53 }
54 }
There’s 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() re-
trieves 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.
7.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. We’ll 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 // Add these import statements:
4 use AlbumModelAlbum;
5 use AlbumModelAlbumTable;
6 use ZendDbResultSetResultSet;
7 use ZendDbTableGatewayTableGateway;
8
7.3. Using ServiceManager to configure the table gateway and inject into the AlbumTable 17
Zend Framework 2 Documentation, Release 2.3.1dev
9 class Module
10 {
11 // getAutoloaderConfig() and getConfig() methods here
12
13 // Add this method:
14 public function getServiceConfig()
15 {
16 return array(
17 ’factories’ => array(
18 ’AlbumModelAlbumTable’ => function($sm) {
19 $tableGateway = $sm->get(’AlbumTableGateway’);
20 $table = new AlbumTable($tableGateway);
21 return $table;
22 },
23 ’AlbumTableGateway’ => function ($sm) {
24 $dbAdapter = $sm->get(’ZendDbAdapterAdapter’);
25 $resultSetPrototype = new ResultSet();
26 $resultSetPrototype->setArrayObjectPrototype(new Album());
27 return new TableGateway(’album’, $dbAdapter, null, $resultSetPrototype);
28 },
29 ),
30 );
31 }
32 }
This method returns an array of factories that are all merged together by the ModuleManager be-
fore passing them 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 cre-
ation 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 2’s ModuleManager merges all the configuration from each
module’s module.config.php file and then merges in the files in config/autoload (*.global.php and
then *.local.php files). We’ll 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 return array(
2 ’db’ => array(
3 ’driver’ => ’Pdo’,
4 ’dsn’ => ’mysql:dbname=zf2tutorial;host=localhost’,
5 ’driver_options’ => array(
6 PDO::MYSQL_ATTR_INIT_COMMAND => ’SET NAMES ’UTF8’’
7 ),
8 ),
9 ’service_manager’ => array(
10 ’factories’ => array(
11 ’ZendDbAdapterAdapter’
12 => ’ZendDbAdapterAdapterServiceFactory’,
13 ),
14 ),
15 );
18 Chapter 7. Database and models
Zend Framework 2 Documentation, Release 2.3.1dev
You should put your database credentials in config/autoload/local.php so that they are not in the git repos-
itory (as local.php is ignored):
1 return array(
2 ’db’ => array(
3 ’username’ => ’YOUR USERNAME HERE’,
4 ’password’ => ’YOUR PASSWORD HERE’,
5 ),
6 );
7.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 // module/Album/src/Album/Controller/AlbumController.php:
2 public function getAlbumTable()
3 {
4 if (!$this->albumTable) {
5 $sm = $this->getServiceLocator();
6 $this->albumTable = $sm->get(’AlbumModelAlbumTable’);
7 }
8 return $this->albumTable;
9 }
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().
7.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 AlbumController’s indexAction() like this:
1 // module/Album/src/Album/Controller/AlbumController.php:
2 // ...
3 public function indexAction()
4 {
5 return new ViewModel(array(
6 ’albums’ => $this->getAlbumTable()->fetchAll(),
7 ));
8 }
9 // ...
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:
7.4. Back to the controller 19
Zend Framework 2 Documentation, Release 2.3.1dev
1 <?php
2 // module/Album/view/album/album/index.phtml:
3
4 $title = ’My albums’;
5 $this->headTitle($title);
6 ?>
7 <h1><?php echo $this->escapeHtml($title); ?></h1>
8 <p>
9 <a href="<?php echo $this->url(’album’, array(’action’=>’add’));?>">Add new album</a>
10 </p>
11
12 <table class="table">
13 <tr>
14 <th>Title</th>
15 <th>Artist</th>
16 <th>&nbsp;</th>
17 </tr>
18 <?php foreach ($albums as $album) : ?>
19 <tr>
20 <td><?php echo $this->escapeHtml($album->title);?></td>
21 <td><?php echo $this->escapeHtml($album->artist);?></td>
22 <td>
23 <a href="<?php echo $this->url(’album’,
24 array(’action’=>’edit’, ’id’ => $album->id));?>">Edit</a>
25 <a href="<?php echo $this->url(’album’,
26 array(’action’=>’delete’, ’id’ => $album->id));?>">Delete</a>
27 </td>
28 </tr>
29 <?php endforeach; ?>
30 </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 browser’s 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 don’t 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 album’s 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).
If you open http://zf2-tutorial.localhost/album you should see this:
20 Chapter 7. Database and models
CHAPTER 8
Styling and Translations
We’ve picked up the SkeletonApplication’s styling, which is fine, but we need to change the title and remove the
copyright message.
The ZendSkeletonApplication is set up to use ZendI18n’s 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 gen-
erated, 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 module’s layout.phtml view script:
1 // module/Application/view/layout/layout.phtml:
2 // Remove this line:
3 <p>&copy; 2005 - 2014 by Zend Technologies Ltd. <?php echo $this->translate(’All
4 rights reserved.’) ?></p>
The page now looks ever so slightly better now!
21
Zend Framework 2 Documentation, Release 2.3.1dev
22 Chapter 8. Styling and Translations
CHAPTER 9
Forms and actions
9.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 class AlbumForm extends Form
6 {
7 public function __construct($name = null)
8 {
9 // we want to ignore the name passed
10 parent::__construct(’album’);
11
12 $this->add(array(
13 ’name’ => ’id’,
14 ’type’ => ’Hidden’,
15 ));
16 $this->add(array(
17 ’name’ => ’title’,
18 ’type’ => ’Text’,
19 ’options’ => array(
20 ’label’ => ’Title’,
21 ),
22 ));
23 $this->add(array(
24 ’name’ => ’artist’,
25 ’type’ => ’Text’,
26 ’options’ => array(
27 ’label’ => ’Artist’,
28 ),
29 ));
30 $this->add(array(
23
Zend Framework 2 Documentation, Release 2.3.1dev
31 ’name’ => ’submit’,
32 ’type’ => ’Submit’,
33 ’attributes’ => array(
34 ’value’ => ’Go’,
35 ’id’ => ’submitbutton’,
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 parent’s
constructor. 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 // Add these import statements
4 use ZendInputFilterInputFilter;
5 use ZendInputFilterInputFilterAwareInterface;
6 use ZendInputFilterInputFilterInterface;
7
8 class Album implements InputFilterAwareInterface
9 {
10 public $id;
11 public $artist;
12 public $title;
13 protected $inputFilter; // <-- Add this variable
14
15 public function exchangeArray($data)
16 {
17 $this->id = (isset($data[’id’])) ? $data[’id’] : null;
18 $this->artist = (isset($data[’artist’])) ? $data[’artist’] : null;
19 $this->title = (isset($data[’title’])) ? $data[’title’] : null;
20 }
21
22 // Add content to these methods:
23 public function setInputFilter(InputFilterInterface $inputFilter)
24 {
25 throw new Exception("Not used");
26 }
27
28 public function getInputFilter()
29 {
30 if (!$this->inputFilter) {
31 $inputFilter = new InputFilter();
32
33 $inputFilter->add(array(
34 ’name’ => ’id’,
35 ’required’ => true,
36 ’filters’ => array(
37 array(’name’ => ’Int’),
38 ),
39 ));
24 Chapter 9. Forms and actions
Zend Framework 2 Documentation, Release 2.3.1dev
40
41 $inputFilter->add(array(
42 ’name’ => ’artist’,
43 ’required’ => true,
44 ’filters’ => array(
45 array(’name’ => ’StripTags’),
46 array(’name’ => ’StringTrim’),
47 ),
48 ’validators’ => array(
49 array(
50 ’name’ => ’StringLength’,
51 ’options’ => array(
52 ’encoding’ => ’UTF-8’,
53 ’min’ => 1,
54 ’max’ => 100,
55 ),
56 ),
57 ),
58 ));
59
60 $inputFilter->add(array(
61 ’name’ => ’title’,
62 ’required’ => true,
63 ’filters’ => array(
64 array(’name’ => ’StripTags’),
65 array(’name’ => ’StringTrim’),
66 ),
67 ’validators’ => array(
68 array(
69 ’name’ => ’StringLength’,
70 ’options’ => array(
71 ’encoding’ => ’UTF-8’,
72 ’min’ => 1,
73 ’max’ => 100,
74 ),
75 ),
76 ),
77 ));
78
79 $this->inputFilter = $inputFilter;
80 }
81
82 return $this->inputFilter;
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
doesn’t enter more characters than we can store into the database.
We now need to get the form to display and then process it on submission. This is done within the
AlbumController’s addAction():
9.1. Adding new albums 25
Zend Framework 2 Documentation, Release 2.3.1dev
1 // module/Album/src/Album/Controller/AlbumController.php:
2
3 //...
4 use ZendMvcControllerAbstractActionController;
5 use ZendViewModelViewModel;
6 use AlbumModelAlbum; // <-- Add this import
7 use AlbumFormAlbumForm; // <-- Add this import
8 //...
9
10 // Add content to this method:
11 public function addAction()
12 {
13 $form = new AlbumForm();
14 $form->get(’submit’)->setValue(’Add’);
15
16 $request = $this->getRequest();
17 if ($request->isPost()) {
18 $album = new Album();
19 $form->setInputFilter($album->getInputFilter());
20 $form->setData($request->getPost());
21
22 if ($form->isValid()) {
23 $album->exchangeArray($form->getData());
24 $this->getAlbumTable()->saveAlbum($album);
25
26 // Redirect to list of albums
27 return $this->redirect()->toRoute(’album’);
28 }
29 }
30 return array(’form’ => $form);
31 }
32 //...
After adding the AlbumForm to the use list, we implement addAction(). Let’s look at the addAction() code
in a little more detail:
1 $form = new AlbumForm();
2 $form->get(’submit’)->setValue(’Add’);
We instantiate AlbumForm and set the label on the submit button to “Add”. We do this here as we’ll want to re-use
the form when editing an album and will use a different label.
1 $request = $this->getRequest();
2 if ($request->isPost()) {
3 $album = new Album();
4 $form->setInputFilter($album->getInputFilter());
5 $form->setData($request->getPost());
6 if ($form->isValid()) {
If the Request object’s isPost() method is true, then the form has been submitted and so we set the form’s
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 $album->exchangeArray($form->getData());
2 $this->getAlbumTable()->saveAlbum($album);
If the form is valid, then we grab the data from the form and store to the model using saveAlbum().
26 Chapter 9. Forms and actions
Zend Framework 2 Documentation, Release 2.3.1dev
1 // Redirect to list of albums
2 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 <?php
2 // module/Album/view/album/album/add.phtml:
3
4 $title = ’Add new album’;
5 $this->headTitle($title);
6 ?>
7 <h1><?php echo $this->escapeHtml($title); ?></h1>
8 <?php
9 $form->setAttribute(’action’, $this->url(’album’, array(’action’ => ’add’)));
10 $form->prepare();
11
12 echo $this->form()->openTag($form);
13 echo $this->formHidden($form->get(’id’));
14 echo $this->formRow($form->get(’title’));
15 echo $this->formRow($form->get(’artist’));
16 echo $this->formSubmit($form->get(’submit’));
17 echo $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().
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.
9.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:
9.2. Editing an album 27
Zend Framework 2 Documentation, Release 2.3.1dev
1 // module/Album/src/Album/Controller/AlbumController.php:
2 //...
3
4 // Add content to this method:
5 public function editAction()
6 {
7 $id = (int) $this->params()->fromRoute(’id’, 0);
8 if (!$id) {
9 return $this->redirect()->toRoute(’album’, array(
10 ’action’ => ’add’
11 ));
12 }
13
14 // Get the Album with the specified id. An exception is thrown
15 // if it cannot be found, in which case go to the index page.
16 try {
17 $album = $this->getAlbumTable()->getAlbum($id);
18 }
19 catch (Exception $ex) {
20 return $this->redirect()->toRoute(’album’, array(
21 ’action’ => ’index’
22 ));
23 }
24
25 $form = new AlbumForm();
26 $form->bind($album);
27 $form->get(’submit’)->setAttribute(’value’, ’Edit’);
28
29 $request = $this->getRequest();
30 if ($request->isPost()) {
31 $form->setInputFilter($album->getInputFilter());
32 $form->setData($request->getPost());
33
34 if ($form->isValid()) {
35 $this->getAlbumTable()->saveAlbum($album);
36
37 // Redirect to list of albums
38 return $this->redirect()->toRoute(’album’);
39 }
40 }
41
42 return array(
43 ’id’ => $id,
44 ’form’ => $form,
45 );
46 }
47 //...
This code should look comfortably familiar. Let’s 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 $id = (int) $this->params()->fromRoute(’id’, 0);
2 if (!$id) {
3 return $this->redirect()->toRoute(’album’, array(
4 ’action’ => ’add’
5 ));
6 }
7
28 Chapter 9. Forms and actions
Zend Framework 2 Documentation, Release 2.3.1dev
8 // Get the album with the specified id. An exception is thrown
9 // if it cannot be found, in which case go to the index page.
10 try {
11 $album = $this->getAlbumTable()->getAlbum($id);
12 }
13 catch (Exception $ex) {
14 return $this->redirect()->toRoute(’album’, array(
15 ’action’ => ’index’
16 ));
17 }
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 $form = new AlbumForm();
2 $form->bind($album);
3 $form->get(’submit’)->setAttribute(’value’, ’Edit’);
The form’s 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 // module/Album/src/Album/Model/Album.php:
2 // ...
3 public function exchangeArray($data)
4 {
5 $this->id = (isset($data[’id’])) ? $data[’id’] : null;
6 $this->artist = (isset($data[’artist’])) ? $data[’artist’] : null;
7 $this->title = (isset($data[’title’])) ? $data[’title’] : null;
8 }
9
10 // Add the following method:
11 public function getArrayCopy()
12 {
13 return get_object_vars($this);
14 }
15 // ...
As a result of using bind() with its hydrator, we do not need to populate the form’s data back into the $album as
that’s 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 <?php
2 // module/Album/view/album/album/edit.phtml:
3
4 $title = ’Edit album’;
5 $this->headTitle($title);
6 ?>
9.2. Editing an album 29
Zend Framework 2 Documentation, Release 2.3.1dev
7 <h1><?php echo $this->escapeHtml($title); ?></h1>
8
9 <?php
10 $form = $this->form;
11 $form->setAttribute(’action’, $this->url(
12 ’album’,
13 array(
14 ’action’ => ’edit’,
15 ’id’ => $this->id,
16 )
17 ));
18 $form->prepare();
19
20 echo $this->form()->openTag($form);
21 echo $this->formHidden($form->get(’id’));
22 echo $this->formRow($form->get(’title’));
23 echo $this->formRow($form->get(’artist’));
24 echo $this->formSubmit($form->get(’submit’));
25 echo $this->form()->closeTag();
The only changes are to use the ‘Edit Album’ title and set the form’s action to the ‘edit’ action too.
You should now be able to edit albums.
9.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 it’s clicked. This would be wrong. Remembering our HTTP spec,
we recall that you shouldn’t 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, we’ll code it directly into our view (ZendForm is, after all, optional!).
Let’s start with the action code in AlbumController::deleteAction():
1 // module/Album/src/Album/Controller/AlbumController.php:
2 //...
3 // Add content to the following method:
4 public function deleteAction()
5 {
6 $id = (int) $this->params()->fromRoute(’id’, 0);
7 if (!$id) {
8 return $this->redirect()->toRoute(’album’);
9 }
10
11 $request = $this->getRequest();
12 if ($request->isPost()) {
13 $del = $request->getPost(’del’, ’No’);
14
15 if ($del == ’Yes’) {
16 $id = (int) $request->getPost(’id’);
17 $this->getAlbumTable()->deleteAlbum($id);
18 }
19
20 // Redirect to list of albums
21 return $this->redirect()->toRoute(’album’);
22 }
30 Chapter 9. Forms and actions
Zend Framework 2 Documentation, Release 2.3.1dev
23
24 return array(
25 ’id’ => $id,
26 ’album’ => $this->getAlbumTable()->getAlbum($id)
27 );
28 }
29 //...
As before, we get the id from the matched route, and check the request object’s 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 <?php
2 // module/Album/view/album/album/delete.phtml:
3
4 $title = ’Delete album’;
5 $this->headTitle($title);
6 ?>
7 <h1><?php echo $this->escapeHtml($title); ?></h1>
8
9 <p>Are you sure that you want to delete
10 ’<?php echo $this->escapeHtml($album->title); ?>’ by
11 ’<?php echo $this->escapeHtml($album->artist); ?>’?
12 </p>
13 <?php
14 $url = $this->url(’album’, array(
15 ’action’ => ’delete’,
16 ’id’ => $this->id,
17 ));
18 ?>
19 <form action="<?php echo $url; ?>" method="post">
20 <div>
21 <input type="hidden" name="id" value="<?php echo (int) $album->id; ?>" />
22 <input type="submit" name="del" value="Yes" />
23 <input type="submit" name="del" value="No" />
24 </div>
25 </form>
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.
9.4 Ensuring that the home page displays the list of albums
One final point. At the moment, the home page, http://zf2-tutorial.localhost/ doesn’t display the list
of albums.
This is due to a route set up in the Application module’s module.config.php. To change it, open
module/Application/config/module.config.php and find the home route:
1 ’home’ => array(
2 ’type’ => ’ZendMvcRouterHttpLiteral’,
3 ’options’ => array(
4 ’route’ => ’/’,
5 ’defaults’ => array(
9.4. Ensuring that the home page displays the list of albums 31
Zend Framework 2 Documentation, Release 2.3.1dev
6 ’controller’ => ’ApplicationControllerIndex’,
7 ’action’ => ’index’,
8 ),
9 ),
10 ),
Change the controller from ApplicationControllerIndex to AlbumControllerAlbum:
1 ’home’ => array(
2 ’type’ => ’ZendMvcRouterHttpLiteral’,
3 ’options’ => array(
4 ’route’ => ’/’,
5 ’defaults’ => array(
6 ’controller’ => ’AlbumControllerAlbum’, // <-- change here
7 ’action’ => ’index’,
8 ),
9 ),
10 ),
That’s it - you now have a fully working application!
32 Chapter 9. Forms and actions
CHAPTER 10
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.
33
Zend Framework 2 Documentation, Release 2.3.1dev
34 Chapter 10. Conclusion
CHAPTER 11
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.
We will develop this application using Zend Studio 10 and run the application on Zend Server 6.
Zend Server is a PHP application server that includes the PHP runtime. It comes in both free and paid editions, both
of which provide lots of features; however the most interesting ones for developers are the dead-simple environment
setup and the ability to investigate application problems, including profiling performance and memory issues with
code-tracing abilities. Zend Server also ships with Zend Framework 2, which is convenient.
Zend Studio is an a PHP-focused IDE based on Eclipse that comes in two flavours: the free Eclipse PDT and Zend
Studio, a paid-for product that provides enhanced features and support options. Usefully, Eclipse PDT provides Zend
Framework 2 support out of the box along with Zend Server integration. You don’t get the mobile features though, or
integrated PHP Documenter & PHPUnit features.
In this tutorial we’re going to build a small, simple database application to manage a list of to-do items. We’ll need a
list of items along with the ability to add, edit and delete items. We’ll use a database to store information about each
to-do item.
11.1 Installation
Firstly you’ll need to install Zend Server and Eclipse PDT. If you have a license for Zend Studio 10, you can use that
too. You can download the latest version of Zend Server. Grab Eclipse PDT or Zend Studio (which comes with a free
30-day trial) and install it. In this tutorial we will use the phrase Zend Studio, but it will all work with Eclipse PDT
too.
On Linux, you can install Zend Server with either Apache or Nginx. This tutorial has assumed that you have installed
the Apache version. The only important difference for this tutorial is the creation of rewrite rules.
Once you have installed Zend Server, enter the administration application, which can usually be found at
http://localhost:10081/. Set the time zone in Configuration -> PHP, and then restart the server (third button from
the right in the top right corner).
You will also need to install MySQL using your Linux distribution’s package manager or from mysql.com if you are
on Windows. For OS X users, Zend Server already includes MySQL for you.
On OS X, the document root for the Zend Server installed Apache is at /usr/local/zend/apache2/htdocs.
On Linux, Zend Server uses the web server supplied by the distribution. On Ubuntu 12.04, with Apache, it is
/var/www and with nginx it is at /usr/share/nginx/html. On Windows, it is C:Program Files
(x86)ZendApache2htdocs.
35
Zend Framework 2 Documentation, Release 2.3.1dev
Ensure that this folder is writeable by your own user. The easiest way to do this is to change the owner of the html
directory. On a Mac, this would be:
$ sudo chown {your username} /usr/local/zend/apache2/htdocs
11.2 Getting Started
We start by creating a new Local PHP project in Zend Studio. Open Zend Studio and select File -> New -> Local PHP
Project. This will display the New Local PHP Project wizard as shown:
Enter MyTaskList as the Project Name and set the location to the Zend Server document root. Due to the integration
between Zend Server and Zend Studio, you should find the correct directory as an option in the drop down list. Select
Zend Framework as the Content and you can then select which version of Zend Framework to use. Select the latest
Zend Framework 2 version and press Next.
The next step is the Launch Settings tab. Choose Launch URL and set the host to http://localhost (or
http://localhost:10088 on OS X) and the Base Path to /MyTaskList/:
Press Finish to create your new project in Zend Studio.
Zend Studio has now created a default Zend Framework project for us:
This is a standard Zend Framework 2 Skeleton Application and is a great starting point for a new ZF2 application.
To set up Zend Studio to run this project, select Run -> Run Configurations... and double click on PHP Web Appli-
cation in the left hand list. Enter MyTaskList as the name, Local Zend Server as the PHP Server and then click the
Browse button and select index.php within the public folder of the MyTaskList project. Uncheck Auto Generate in the
URL section and then set the path to /MyTaskList/public and press Apply and then Close:
To test that all is working, press the run button in the toolbar (white arrow in a green circle). The ZF2 Skeleton
Application home page will display in a new tab within Zend Studio:
You can also navigate to the same URL (http://localhost:10088/MyTaskList/public/ on a Mac) in any browser.
We have successfully installed both Zend Server and Zend Studio, created a project and tested it. Let’s start by looking
at what we have so far in our Zend Framework project.
36 Chapter 11. Getting Started with Zend Framework 2
CHAPTER 12
A quick tour of the skeleton application
The skeleton application provides a lot of files, so it’s worth having a quick high-level look at what has been generated
for us. There are a number of high level directories created for us (along with Composer and other support files):
Folder Information stored
config Application-level configuration files.
data Data files generated by the application, such as caches.
module The source files that make up this application are stored within separate modules within this folder.
public The web server’s document root. All files served directly by the web server are in here.
vendor Third party libraries.
One of the key features of Zend Framework 2 is its module system. This provides organisation within your application;
all application code lives within a module. The skeleton provides the Application module for bootstrapping, error and
routing configuration. It also provides the application-level controllers for the home page and error display. The
Application module contains these key folders:
Folder Information stored
config Module-specific configuration files.
language Translation files.
src/ApplicationPHP files for this module, including controller and model files. The controller for the home
page, IndexController.php, is provided.
view/applicationView scripts for each controller action.
view/error Error view scripts for 404 and generic errors.
view/layout Layout view scripts. These contain the common HTML shared by a number of pages within the
website. An initial default file, layout.phtml, is provided.
Modules are simply namespaces containing a top level Module class. They are intended to be reusable and no
additional constraints are placed on how they are organised. An application consists of multiple modules, both third
party and application specific, with the list of modules to load stored in config/application.config.php.
12.1 The dispatch cycle
Zend Framework 2 applications use the Front Controller design pattern. This means that all requests are directed to
a single entry point, the public/index.php file. This is done using a .htaccess file containing rewrite rules that
serves all static files (such as CSS & Javascript) and directs all other requests to the index.php. The index.php file
initialises the autoloader and then bootstraps ZendMvcApplication before finally running the application. The
process looks like this:
37
Zend Framework 2 Documentation, Release 2.3.1dev
12.1.1 Starting up
To set up the application for running, a number of things happen. Firstly an instance of ZendServiceManger is
created as the master locator for all class instances used by the application. The Module Manager is then used to load
all the application’s modules. It does this by reading its configuration file, application.config.php, which is
solely for use by the Module Manager and does not contain the configuration used by the application itself.
The modules are loaded in the order listed in the configuration file and for each module a number of steps takes place:
• Configuration of autoloading.
• Loading of module configuration.
• Registration of event listeners.
• Configuration of the Service Manager.
The configuration information from all modules is merged together into one configuration array. This means that con-
figuration information in subsequent modules can override information already set. Finally, the global configuration
files stored in the config/autoload directory are merged (the *.global.php and then the *.local.php
files). This means that any module’s configuration can be overridden at the application level and is a key feature that
helps to ensure that the code within a third-party module does not need to be changed.
The Service Manager and Event Manager are two other key features of a Zend Framework 2 application.
ZendServiceManager allows for decoupling the instantiation and configuration of a class and its dependencies from
where that class is used. This is known as Dependency Injection and is used extensively in Zend Framework 2.
ZendEventManager is an implementation of the Observer design pattern which allows decoupling of code. In Zend
Framework 2, every key process in the dispatch cycle is implemented as an event. This means that you can write lis-
teners for these events which can then change the flow of operation or perform additional processes when something
else has happened.
12.1.2 Dispatching
Once all modules have been loaded, the application is run. This is done as a series of events, with the first event,
route, used to determine the controller action that should be run based on the URL requested. Once this is determined,
the dispatch event is triggered which causes the action method within the controller class to be executed. The view
rendering event, render, is then triggered if an HTML view is required. Finally the finish event is triggered which
sends the response back to the user’s web browser.
While this is a typical dispatch cycle, Zend Framework 2’s dispatch system is very flexible and can be configured in
a variety of ways depending on the specific application. Now that we’ve looked at how Zend Framework works, let’s
move on and write the MyTaskList application.
38 Chapter 12. A quick tour of the skeleton application
CHAPTER 13
The MyTaskList application
The application we are going to create is a to-do list manager. The application will allow us to create to-do items and
check them off. We’ll also need the ability to edit and delete an item. As we are building a simple application, we
need just four pages:
Page Notes
Checklist homepage This will display the list of to-do items.
Add new item This page will provide a form for adding a new item.
Edit item This page will provide a form for editing an item.
Delete item This page will confirm that we want to delete an item and then delete it.
Each page of the application is known as an action, and actions are grouped into controllers within modules. Generally,
related actions are placed into a single controller; for instance, a news controller might have actions of current,
archived and view.
We will store information about our to-do items in a database. A single table will suffice with the following fields:
Field name Type Null? Notes
id integer No Primary key, auto-increment
title varchar(100) No Name of the file on disk
completed tinyint No Zero if not done, one if done
created datetime No Date that the to-do item was created
We are going to use MySQL, via PHP’s PDO driver, so create a database called mytasklist using your preferred
MySQL client, and run these SQL statements to create the task_item table and some sample data:
CREATE TABLE task_item (
id INT NOT NULL AUTO_INCREMENT,
title VARCHAR(100) NOT NULL,
completed TINYINT NOT NULL DEFAULT ’0’,
created DATETIME NOT NULL,
PRIMARY KEY (id)
);
INSERT INTO task_item (title, completed, created)
VALUES (’Purchase conference ticket’, 0, NOW());
INSERT INTO task_item (title, completed, created)
VALUES (’Book airline ticket’, 0, NOW());
INSERT INTO task_item (title, completed, created)
VALUES (’Book hotel’, 0, NOW());
INSERT INTO task_item (title, completed, created)
VALUES (’Enjoy conference’, 0, NOW());
39
Zend Framework 2 Documentation, Release 2.3.1dev
Note that if you have Zend Studio, you can use the built-in Database Connectivity features. This if found in the
Database Development perspective (Window | Open Perspective | Other | Database Development menu item) and
further details are in the Zend Studio manual.
13.1 The Checklist module
We will create all our code within a module called Checklist. The Checklist module will, therefore, contain our
controllers, models, forms and views, along with specific configuration files.
We create our new Checklist module in Zend Studio. In the PHP Explorer on the left, right click on the MyTaskList
project folder and choose New -> Zend Framework Item. Click on Zend Module and press Next. The Source Folder
should already be set to /MyTaskList/module. Enter Checklist as the Module name and Task as the Controller
name and then press Finish:
The wizard will now go ahead and create a blank module for us and register it with the Module Manager’s
application.config.php. You can see what it has done in the PHP Explorer view under the module folder:
As you can see the Checklist module has separate directories for the different types of files we will have. The config
folder contains configuration files, and the PHP files that contain classes within the Checklist namespace live in
the src/Checklist directory. The view directory also has a sub- folder called checklist for our module’s
view scripts, and the tests folder contains PHPUnit test files.
13.2 The Module class
As mentioned earlier, a module’s Module class contains methods that are called during the start-up process and is
also used to register listeners that will be triggered during the dispatch process. The Module class created for us
contains three methods: getAutoloaderConfig(), getConfig() and onBootstrap() which are called
by the Module Manager during start-up.
13.2.1 Autoloading files
Our getAutoloaderConfig() method returns an array that is compatible with ZF2’s AutoloaderFactory.
It is configured for us with both a classmap file (autoload_classmap.php) and a standard autoloader to load
any files in src/Checklist according to the PSR-0 rules .
Classmap autoloading is faster, but requires adding each new class you create to the array within the au-
toload_classmap.php file, which slows down development. The standard autoloader, however, doesn’t have this re-
quirement and will always load a class if its file is named correctly. This allows us to develop quickly by creating new
classes when we need them and then gain a performance boost by using the classmap autoloader in production. Zend
Framework 2 provides bin/classmap_generator.php to create and update the file.
13.2.2 Configuration
The getConfig() method in ChecklistModule is called by the Module Manager to retrieve the configuration
information for this module. By tradition, this method simply loads the config/module.config.php file which
is an associative array. In practice, the Module Manager requires that the returned value from getConfig() be
a Traversable, which means that you can use any configuration format that ZendConfig supports. You will
find, though, that most examples use arrays as they are easy to understand and fast.
The actual configuration information is placed in config/module.config.php. This nested array provides the
key configuration for our module. The controllers sub-array is used to register this module’s controller classes
40 Chapter 13. The MyTaskList application
Zend Framework 2 Documentation, Release 2.3.1dev
with the Controller Service Manager which is used by the dispatcher to instantiate a controller. The one controller that
we need, TaskController, is already registered for us.
The router sub-array provides the configuration of the routes that are used by this module. A route is the way that a
URL is mapped to a to a particular action method within a controller class. Zend Studio’s default configuration is set
up so that a URL of /checklist/foo/bar maps to the barAction() method of the FooController within
the Checklist module. We will modify this later.
Finally, the view_manager sub-array within the module.config.php file is used to register the directory
where our view files are with the View sub- system. This means that within the view/checklist sub-folder,
there is a folder for each controller. We have one controller, TaskController, so there is a single sub-folder in
view/checklist called task. Within this folder, there are separate .phtml files which contain the specific
HTML for each action of our module.
13.2.3 Registering events
The onBootstrap() method in the Module class is the easiest place to register listeners for the MVC events that
are triggered by the Event Manager. Note that the default method body provided by Zend Studio is not needed as
the ModuleRouteListener is already registered by the Application module. We do not have to register any
events for this tutorial, so go ahead and delete the entire OnBootstrap() method.
13.2. The Module class 41
Zend Framework 2 Documentation, Release 2.3.1dev
42 Chapter 13. The MyTaskList application
CHAPTER 14
The application’s pages
As we have four pages that all apply to tasks, we will group them in a single controller called TaskController
within our Checklist module as four actions. Each action has a related URL which will result in that action being
dispatched. The four actions and URLs are:
Page URL Action
Homepage /task index
Add new task /task/add add
Edit task /task/edit edit
Delete task /task/delete delete
The mapping of a URL to a particular action is done using routes that are defined in the module’s
module.config.php file. As noted earlier, the configuration file, module.config.php created by Zend Stu-
dio has a route called checklist set up for us.
14.1 Routing
The default route provided for us isn’t quite what we need. The checklist route is defined like this:
module/Checklist/src/config/module.config.php:
’router’ => array(
’routes’ => array(
’checklist’ => array(
’type’ => ’Literal’,
’options’ => array(
’route’ => ’/task’,
’defaults’ => array(
’__NAMESPACE__’ => ’ChecklistController’,
’controller’ => ’Task’,
’action’ => ’index’,
),
),
’may_terminate’ => true,
’child_routes’ => array(
’default’ => array(
’type’ => ’Segment’,
’options’ => array(
’route’ => ’/[:controller[/:action]]’,
),
),
43
Zend Framework 2 Documentation, Release 2.3.1dev
),
),
This defines a main route called checklist, which maps the URL /task to the index action of the Task controller
and then there is a child route called default which maps /task/{controller name}/{action name}
to the {action name} action of the {controller name} controller. This means that, by default, the URL to call the add
action of the Task controller would be /task/task/add. This doesn’t look very nice and we would like to shorten
it to /task/add.
To fix this, we will rename the route from checklist to task because this route will be solely for the Task
controller. We will then redefine it to be a single Segment type route that can handle actions as well as just route to
the index action
Open module/Checklist/config/module.config.php in Zend Studio and change the entire router sec-
tion of the array to be:
module/Checklist/src/config/module.config.php:
’router’ => array(
’routes’ => array(
’task’ => array(
’type’ => ’Segment’,
’options’ => array(
’route’ => ’/task[/:action[/:id]]’,
’defaults’ => array(
’__NAMESPACE__’ => ’ChecklistController’,
’controller’ => ’Task’,
’action’ => ’index’,
),
’constraints’ => array(
’action’ => ’^add|edit|delete$’,
’id’ => ’[0-9]+’,
),
),
),
),
),
We have now renamed the route to task and have set it up as a Segment route with two optional parameters in the
URL: action and id. We have set a default of index for the action, so that if the URL is simply /task, then
we shall use the index action in our controller.
The optional constraints section allow us to specify regular expression patterns that match the characters that we
expect for a given parameter. For this route, we have specified that the action parameter must be either add, edit or
delete and that the id parameter must only contain numbers.
The routing for our Checklist module is now set up, so we can now turn our attention to the controller.
14.2 The TaskController
In Zend Framework 2, the controller is a class that is generally called {Controller name}Controller.
Note that {Controller name} starts 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’s the
module/Checklist/src/Checklist/Controller directory. Each action is a public function within the
controller class that is named {action name}Action. In this case {action name} should start with a lower
case letter.
44 Chapter 14. The application’s pages
Zend Framework 2 Documentation, Release 2.3.1dev
Note that this is merely a convention. Zend Framework 2’s only restrictions on a controller is that it must implement
the ZendStdlibDispatchable interface. The framework provides two abstract classes that do this for us:
ZendMvcControllerActionController and ZendMvcControllerRestfulController.
We’ll be using the ActionController, but if you’re intending to write a RESTful web service,
RestfulController may be useful.
Zend Studio’s module creation wizard has already created TaskController for us with two action methods in
it: indexAction() and fooAction(). Remove the fooAction() method and the default “Copyright Zend”
DocBlock comment at the top of the file. Your controller should now look like this:
module/Checklist/src/Checklist/Controller/TaskController.php:
namespace ChecklistController;
use ZendMvcControllerAbstractActionController;
class TaskController extends AbstractActionController
{
public function indexAction()
{
return array();
}
}
This controller now contains the action for the home page which will display our list of to-do items. We now need to
create a model-layer that can retrieve the tasks from the database for display.
14.3 The model
It is time to look at the model section of our application. Remember that the model is the part that deals with the
application’s core purpose (the so-called “business rules”) and, in our case, deals with the database. Zend Framework
does not provide a ZendModel component because the model is your business logic and it’s 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 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 fairly simple model layer using an entity and a mapper that uses the ZendDb component. In a
larger, more complex, application, you would probably also have a service class that interfaces between the controller
and the mapper.
We already have created the database table and added some sample data, so let’s start by creating an entity object. An
entity object is a simple PHP object that represents a thing in the application. In our case, it represents a task to be
completed, so we will call it TaskEntity.
Create a new folder in module/Checklist/src/Checklist called Model and then right click on the new
Model folder and choose New -> PHP File. In the New PHP File dialog, set the File Name to TaskEntity.php
as shown and then press Finish.
This will create a blank PHP file. Update it so that it looks like this:
module/Checklist/src/Checklist/TaskEntity.php:
<?php
namespace ChecklistModel;
class TaskEntity
14.3. The model 45
Zend Framework 2 Documentation, Release 2.3.1dev
{
protected $id;
protected $title;
protected $completed = 0;
protected $created;
public function __construct()
{
$this->created = date(’Y-m-d H:i:s’);
}
public function getId()
{
return $this->id;
}
public function setId($Value)
{
$this->id = $Value;
}
public function getTitle()
{
return $this->title;
}
public function setTitle($Value)
{
$this->title = $Value;
}
public function getCompleted()
{
return $this->completed;
}
public function setCompleted($Value)
{
$this->completed = $Value;
}
public function getCreated()
{
return $this->created;
}
public function setCreated($Value)
{
$this->created = $Value;
}
}
The Task entity is a simple PHP class with four properties with getter and setter methods for each property. We also
have a constructor to fill in the created property. If you are using Zend Studio rather than Eclipse PDT, then you
can generate the getter and setter methods by right clicking in the file and choosing Source -> Generate Getters and
Setters.
We now need a mapper class which is responsible for persisting task entities to the database and populating them
46 Chapter 14. The application’s pages
Zend Framework 2 Documentation, Release 2.3.1dev
with new data. Again, right click on the Model folder and choose New -> PHP File and create a PHP file called
TaskMapper.php. Update it so that it looks like this:
module/Checklist/src/Checklist/TaskMapper.php:
<?php
namespace ChecklistModel;
use ZendDbAdapterAdapter;
use ChecklistModelTaskEntity;
use ZendStdlibHydratorClassMethods;
use ZendDbSqlSql;
use ZendDbSqlSelect;
use ZendDbResultSetHydratingResultSet;
class TaskMapper
{
protected $tableName = ’task_item’;
protected $dbAdapter;
protected $sql;
public function __construct(Adapter $dbAdapter)
{
$this->dbAdapter = $dbAdapter;
$this->sql = new Sql($dbAdapter);
$this->sql->setTable($this->tableName);
}
public function fetchAll()
{
$select = $this->sql->select();
$select->order(array(’completed ASC’, ’created ASC’));
$statement = $this->sql->prepareStatementForSqlObject($select);
$results = $statement->execute();
$entityPrototype = new TaskEntity();
$hydrator = new ClassMethods();
$resultset = new HydratingResultSet($hydrator, $entityPrototype);
$resultset->initialize($results);
return $resultset;
}
}
Within this mapper class we have implemented the fetchAll() method and a constructor. There’s quite a lot
going on here as we’re dealing with the ZendDb component, so let’s break it down. Firstly we have the construc-
tor which takes a ZendDbAdapterAdapter parameter as we can’t do anything without a database adapter.
ZendDbSql is an object that abstracts SQL statements that are compatible with the underlying database adapter
in use. We are going to use this object for all of our interaction with the database, so we create it in the constructor.
The fetchAll() method retrieves data from the database and places it into a HydratingResultSet which is
able to return populated TaskEntity objects when iterating. To do this, we have three distinct things happening.
Firstly we retrieve a Select object from the Sql object and use the order() method to place completed items last.
We then create a Statement object and execute it to retrieve the data from the database. The $results object can
be iterated over, but will return an array for each row retrieved but we want a ‘‘ TaskEntity‘‘ object. To get this, we
create a HydratingResultSet which requires a hydrator and an entity prototype to work.
The hydrator is an object that knows how to populate an entity. As there are many ways to create an entity object,
there are multiple hydrator objects provided with ZF2 and you can create your own. For our TaskEntity, we use
14.3. The model 47
Zend Framework 2 Documentation, Release 2.3.1dev
the ClassMethods hydrator which expects a getter and a setter method for each column in the resultset. Another
useful hydrator is ArraySerializable which will call getArrayCopy() and populate() on the entity
object when transferring data. The HydratingResultSet uses the prototype design pattern when creating the
entities when iterating. This means that instead of instantiating a new instance of the entity class on each iteration,
it clones the provided instantiated object. See http://ralphschindler.com/2012/03/09/php- constructor-best-practices-
and-the-prototype-pattern for more details.
Finally, fetchAll() returns the result set object with the correct data in it.
14.4 Using Service Manager to configure the database credentials
and inject into the controller
In order to always use the same instance of our TaskMapper, we will use the Service Manager to define how to
create the mapper and also to retrieve it when we need it. This is most easily done in the Module class where we
create a method called getServiceConfig() which is automatically called by the Module Manager and applied
to the Service Manager. We’ll then be able to retrieve it in our controller when we need it.
To configure the Service Manager we can either supply the name of the class to be instantiated or create a factory
(closure or callback) method that instantiates the object when the Service Manager needs it. We start by implementing
getServiceConfig() and write a closure that creates a TaskMapper instance. Add this method to the Module class:
** module/Checklist/Module.php:**
class Module
{
public function getServiceConfig()
{
return array(
’factories’ => array(
’TaskMapper’ => function ($sm) {
$dbAdapter = $sm->get(’ZendDbAdapterAdapter’);
$mapper = new TaskMapper($dbAdapter);
return $mapper;
}
),
);
}
// ...
Don’t forget to add use ChecklistModelTaskMapper; to the list of use statements at the top of the file.
The getServiceConfig() method returns an array of class creation definitions that are all merged together by
the Module Manager before passing to the Service Manager. To create a service within the Service Manager we use
a unique key name, TaskMapper. As this has to be unique, it’s common (but not a requirement) to use the fully
qualified class name as the Service Manager key name. We then define a closure that the Service Manager will call
when it is asked for an instance of TaskMapper. We can do anything we like in this closure, as long as we return an
instance of the required class. In this case, we retrieve an instance of the database adapter from the Service Manager
and then instantiate a TaskMapper object and return it. This is an example of the Dependency Injection pattern at
work as we have injected the database adapter into the mapper. This also means that Service Manager can be used as
a Dependency Injection Container in addition to a Service Locator.
As we have requested an instance of ZendDbAdapterAdapter from the Service Manager, we also need
to configure the Service Manager so that it knows how to instantiate a ZendDbAdapterAdapter. This is
done using a class provided by Zend Framework called ZendDbAdapterAdapterServiceFactory which
we can configure within the merged configuration system. As we noted earlier, the Module Manager merges all the
configuration from each module and then merges in the files in the config/autoload directory (*.global.php
48 Chapter 14. The application’s pages
Zend Framework 2 Documentation, Release 2.3.1dev
and then *.local.php files). We’ll add our database configuration information to global.php which you should
commit to your version control system.You can then use local.php (outside of the VCS) to store the credentials for
your database.
Open config/autoload/global.php and replace the empty array with:
config/autoload/global.php:
return array(
’service_manager’ => array(
’factories’ => array(
’ZendDbAdapterAdapter’ =>
’ZendDbAdapterAdapterServiceFactory’,
),
),
’db’ => array(
’driver’ => ’Pdo’,
’dsn’ => ’mysql:dbname=mytasklist;hostname=localhost’,
’driver_options’ => array(
PDO::MYSQL_ATTR_INIT_COMMAND => ’SET NAMES ’UTF8’’
),
),
);
Firstly, we provide additional Service Manager configuration in the service_manager section, This array works
exactly the same as the one in getServiceConfig(), except that you should not use closures in a con-
fig file as if you do Module Manager will not be able to cache the merged configuration information. As
we already have an implementation for creating a ZendDbAdapterAdapter, we use the factories
sub-array to map the key name of ZendDbAdapterAdapter to the string name of the factory class
(ZendDbAdapterAdapterServiceFactory‘) and the Service Manager will then use ZendDbAdapter-
AdapterServiceFactory to instantiate a database adapter for us.
The ZendDbAdapterAdapterServiceFactory object looks for a key called db in the configuration array
and uses this to configure the database adapter. Therefore, we create the db key in our global.php file with the
relevant configuration data. The only data that is missing is the username and password required to connect to the
database. We do not want to store this in the version control system, so we store this in the local.php configuration
file, which, by default, is ignored by git.
Open config/autoload/local.php and replace the empty array with:
config/autoload/global.php:
return array(
’db’ => array(
’username’ => ’YOUR_USERNAME’,
’password’ => ’YOUR_PASSWORD’,
),
);
Obviously you should replace YOUR_USERNAME and YOUR_PASSWORD with the correct credentials.
Now that the Service Manager can create a TaskMapper instance for us, we can add a method to the controller to
retrieve it. Add getTaskMapper() to the TaskController class:
module/Checklist/src/Checklist/Controller/TaskController.php:
public function getTaskMapper()
{
$sm = $this->getServiceLocator();
return $sm->get(’ChecklistModelTaskMapper’);
}
14.4. Using Service Manager to configure the database credentials and inject into the controller 49
Zend Framework 2 Documentation, Release 2.3.1dev
We can now call getTaskMapper() from within our controller whenever we need to interact with our model layer.
Let’s start with a list of tasks when the index action is called.
50 Chapter 14. The application’s pages
CHAPTER 15
Listing tasks
In order to list the tasks, we need to retrieve them from the model layer and pass them to the view. To do this, we fill
in indexAction() within TaskController. Update the indexAction() like this:
module/Checklist/src/Checklist/Controller/TaskController.php:
public function indexAction()
{
$mapper = $this->getTaskMapper();
return new ViewModel(array(’tasks’ => $mapper->fetchAll()));
}
You’ll also need to add use ZendViewModelViewModel; to list of use statements at the top of the file.
To provide variables to the view layer, 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}. You can also return an array from a controller as Zend Framework will construct a
ViewModel behind the scenes for you.
We can now fill in the task/index.phtml view script. Replace the contents with this new code:
module/Checklist/view/checklist/task/index.phtml:
<?php
$title = ’My task list’;
$this->headTitle($title);
?>
<h1><?php echo $this->escapeHtml($title); ?></h1>
<p><a href="<?php echo $this->url(’task’, array(
’action’=>’add’));?>">Add new item</a></p>
<table class="table">
<tr>
<th>Task</th>
<th>Created</th>
<th>Completed?</th>
<th>&nbsp;</th>
</tr>
<?php foreach ($tasks as $task): ?>
<tr>
<td>
<a href="<?php echo $this->url(’task’,
array(’action’=>’edit’, ’id’ => $task->getId()));?>">
51
Zend Framework 2 Documentation, Release 2.3.1dev
<?php echo $this->escapeHtml($task->getTitle()); ?></a>
</td>
<td><?php echo $this->escapeHtml($task->getCreated()); ?></td>
<td><?php echo $task->getCompleted() ? ’Yes’ : ’No’; ?></td>
<td>
<a href="<?php echo $this->url(’task’,
array(’action’=>’delete’, ’id’ => $task->getId()));?>">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 browser’s title bar. We then create a link to add a new
item using the url() view helper.
The url() view helper is provided by Zend Framework and is used to create the links we need. The first parameter
to url() is the route name that we wish to use for construction of the URL and then the second parameter is an array
of all the variables to fit into the place-holders to use. In this case we use our task route which is set up to accept two
place-holder variables: action and id.
We iterate over the $tasks that we assigned from the controller action within an HTML table. The Zend Framework
view system automatically ensures that these variables are extracted into the scope of the view script. Alternatively,
you can also prefix with $this-> if you would like.
For each row, we display each task’s title, creation date, completion date and provide links to allow for editing and
deleting the record. A standard foreach: loop is used to iterate over the list of tasks, 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 that we always use the escapeHtml() view helper to help protect ourselves from XSS vulnerabilities.
If you now run the application from within Zend Studio and navigate to http://localhost:10088/MyTaskList/public/task
you should see this:
15.1 Redirect the home page
When you first pressed the Run button, you saw the application’s home page which is the skeleton’s welcome page. It
would be helpful if we could redirect immediately to /tasks to save us having to edit the URL each time.
To do this, go to Navigate -> Open Type... in Zend Studio and type IndexController
in the search box of the Open PHP Type dialog and press return. This will open
module/Application/src/Application/Controller/IndexController.php for you. Change
the indexAction() method so that it reads:
module/Application/src/Application/Controller/IndexController.php:
public function indexAction()
{
return $this->redirect()->toRoute(’task’);
}
We use the redirect controller plugin to redirect the request for the home page to the URL defined by the route
name task which we set up earlier. Now, when you press the green “Run” button, you will be taken directly to the list
of tasks.
52 Chapter 15. Listing tasks
CHAPTER 16
Styling
We’ve picked up the skeleton application’s layout which is fine for this tutorial, but we need to change the title and
remove the copyright message.
The Zend Skeleton Application is set up to use ZendI18n‘s translation functionality for all the text. This allows
you to translate all the text strings in the application into a different language if you need to.
The translation data is stored in separate files in the gettext format which have the extension .po and are
stored in the application/language folder. The title of the application is “Skeleton Application” and to
change this, you need to use the poedit application (http://www.poedit.net/download.php/). Start poedit and open
application/language/en_US.po. Click on “Skeleton Application” in the list of original strings and then
type in “My Task List” as the translation.
Press Save in the toolbar and poedit will create an updated en_US.mo file.
Alternatively, the gted Eclipse plugin allows for editing PO files directly in Zend Studio or PDT. To install gted,
select the Help > Install New Software menu, and press the “Add...” button. Enter the gted for the Name,
http://gted.sourceforge.net/update as the Location and then press the “OK” button. You will see the gted name ap-
pear in the list. Click on the checkbox next to gted and work through the install wizard by pressing “Next button as
required. At the end of the installation you will be able to create or edit the PO files using the gted plugin:
It follows that as Zend Studio and PDT are based on Eclipse you can install any other Eclipse plugins that are listed
on http://marketplace.eclipse.org/ using the same process.
The next thing to do is to remove the copyright message, we need to edit the Application module’s layout.phtml
view script:
module/Application/view/layout/layout.phtml:
Remove this line:
<p>&copy; 2005 - <?php echo date(’Y’) ?> by Zend Technologies Ltd. <?php echo $this->translate(’All r
The page looks a little better now!
53
Zend Framework 2 Documentation, Release 2.3.1dev
54 Chapter 16. Styling
CHAPTER 17
Adding new tasks
We can now write the functionality to add new tasks. There are two things we need to do:
• Display a form for user to provide the task information
• Process the form submission and store to database
We use ZendForm to do this. The ZendForm component manages the form and works in tandem with the
ZendInputFilter component which will provide validation.
Create a new folder in module/Checklist/src/Checklist called Form and then within the Form folder,
create a new PHP file called TaskForm.php with these contents:
module/Checklist/src/Checklist/Form/TaskForm.php:
<?php
namespace ChecklistForm;
use ZendFormForm;
use ZendStdlibHydratorClassMethods;
class TaskForm extends Form
{
public function __construct($name = null, $options = array())
{
parent::__construct(’task’);
$this->setAttribute(’method’, ’post’);
$this->setInputFilter(new TaskFilter());
$this->setHydrator(new ClassMethods());
$this->add(array(
’name’ => ’id’,
’type’ => ’hidden’,
));
$this->add(array(
’name’ => ’title’,
’type’ => ’text’,
’options’ => array(
’label’ => ’Title’,
),
’attributes’ => array(
’id’ => ’title’,
’maxlength’ => 100,
)
55
Zend Framework 2 Documentation, Release 2.3.1dev
));
$this->add(array(
’name’ => ’completed’,
’type’ => ’checkbox’,
’options’ => array(
’label’ => ’Completed?’,
’label_attributes’ => array(’class’=>’checkbox’),
),
));
$this->add(array(
’name’ => ’submit’,
’attributes’ => array(
’type’ => ’submit’,
’value’ => ’Go’,
’class’ => ’btn btn-primary’,
),
));
}
}
Within the constructor of TaskForm, we set the name when we call the parent’s constructor and then set the method
and the input filter that we want to use. We also set the form’s hydrator to be ClassMethods, as a form object
uses hydration to transfer data to and from an entity object in exactly the same way as the ZendDb components do.
Finally, we create the form elements for the id, title, whether the task is complete and the 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 is this done using an input filter which can either
be standalone or within any class that implements InputFilterAwareInterface, such as a model entity. For
this application we are going to create a separate class for our input filter.
Create a new PHP file called TaskFilter.php in the module/Checklist/src/Checklist/Form folder
with these contents:
module/Checklist/src/Checklist/Form/TaskFilter.php:
<?php
namespace ChecklistForm;
use ZendInputFilterInputFilter;
class TaskFilter extends InputFilter
{
public function __construct()
{
$this->add(array(
’name’ => ’id’,
’required’ => true,
’filters’ => array(
array(’name’ => ’Int’),
),
));
$this->add(array(
’name’ => ’title’,
’required’ => true,
’filters’ => array(
array(’name’ => ’StripTags’),
56 Chapter 17. Adding new tasks
Zend Framework 2 Documentation, Release 2.3.1dev
array(’name’ => ’StringTrim’),
),
’validators’ => array(
array(
’name’ => ’StringLength’,
’options’ => array(
’encoding’ => ’UTF-8’,
’max’ => 100
),
),
),
));
$this->add(array(
’name’ => ’completed’,
’required’ => false,
));
}
}
In the constructor for the TaskFilter, we create inputs for each property that we want to filter. Each input can have
a name, a required property a list of filters and a list of validators. All are optional other than the name property. The
difference between filters and validators is that a filter changes the data passed through it and a validator tests if the
data matches some specific criteria. For the title, we filter the string with StripTags and StringTrim and finally
ensure that the string is no longer than 100 characters with the StringLength validator. For the completed element,
we simply set required to false.
We now need to display the form and process it on submission. This is done within the TaskController‘s
addAction(). Open TaskController.php (Navigate -> Open Resource... is a convenient way to do this)
and add a new method called addAction() to the class that looks like this:
module/Checklist/src/Checklist/Controller/TaskController.php:
public function addAction()
{
$form = new TaskForm();
$task = new TaskEntity();
$form->bind($task);
$request = $this->getRequest();
if ($request->isPost()) {
$form->setData($request->getPost());
if ($form->isValid()) {
$this->getTaskMapper()->saveTask($task);
// Redirect to list of tasks
return $this->redirect()->toRoute(’task’);
}
}
return array(’form’ => $form);
}
Add use ChecklistModelTaskEntity; and use ChecklistFormTaskForm; to the list of use
statements at the top of the file.
Let’s look at what the addAction() does in detail.
57
Zend Framework 2 Documentation, Release 2.3.1dev
$form = new TaskForm();
$task = new TaskEntity();
$form->bind($task);
We instantiate a new TaskForm object and an empty TaskEntity which we bind to the form for use by the form
later. The form’s bind() method attaches the model to the form. This is used in two ways:
1. When displaying the form, the initial values for each element are extracted from the model.
2. After successful validation in isValid(), the data from the form is put back into the model.
When adding a new task, we only need to worry about point 2, however for editing an item, we need data transfer in
both directions.
$request = $this->getRequest();
if ($request->isPost()) {
$form->setData($request->getPost());
if ($form->isValid()) {
For a submitted form, we set the posted data to the form and check to see if it is valid using the isValid() member
function of the form. The isValid() method uses the form’s input filter to test for validity and if it returns true, it
will then transfer the filtered data values to the entity object that is bound to the form using the registered hydrator.
This means that after isValid() is called, $task now contains the submitted form data.
$this->getTaskMapper()->saveTask($task);
As the form is valid, we can save $task to the database using the mapper’s saveTask() method.
// Redirect to list of tasks
return $this->redirect()->toRoute(’task’);
After we have saved the new task, we redirect back to the list of tasks using the Redirect controller plugin.
return array(’form’ => $form);
Finally, if this request is not a POST, we return the variables that we want assigned to the view. In this case, just the
form object.
We also need to add the saveTask() method to the TaskMapper class. Open
module/Checklist/src/Checklist/Model/TaskMapper.php and add this method to the end of
the class:
module/Checklist/src/Checklist/Model/TaskMapper.php:
public function saveTask(TaskEntity $task)
{
$hydrator = new ClassMethods();
$data = $hydrator->extract($task);
if ($task->getId()) {
// update action
$action = $this->sql->update();
$action->set($data);
$action->where(array(’id’ => $task->getId()));
} else {
// insert action
$action = $this->sql->insert();
unset($data[’id’]);
$action->values($data);
}
$statement = $this->sql->prepareStatementForSqlObject($action);
58 Chapter 17. Adding new tasks
Zend Framework 2 Documentation, Release 2.3.1dev
$result = $statement->execute();
if (!$task->getId()) {
$task->setId($result->getGeneratedValue());
}
return $result;
}
The saveTask() method handles both inserting a new record if $task doesn’t have an id or updating it if it
does. In either case, we need the data from the entity as an array, so we can use the hydrator to do this. If we are
updating, then we use the Sql object’s update() method to create an Update object where we can set the data
and a where clause. For inserting, we need an Insert object to which we set the values. Obviously, when inserting,
the database will auto-increment the id, so we do not need the id property in the values list. In either case, we create
a statement object and then execute it. Finally, if we are inserting, we populate the task entity’s id with the value of
the auto-generated id.
We now need to render the form in the add.phtml view script. Create a new PHP file called add.phtml in the
module/Checklist/view/checklist/task folder and add this code:
module/Checklist/view/checklist/task/add.phtml:
<?php
$title = ’Add new task’;
$this->headTitle($title);
?>
<h1><?php echo $this->escapeHtml($title); ?></h1>
<?php
$form = $this->form;
$form->setAttribute(’action’, $this->url(’task’, array(’action’ => ’add’)));
$form->get(’submit’)->setAttribute(’value’, ’Add’);
$form->prepare();
echo $this->form()->openTag($form);
echo $this->formHidden($form->get(’id’));
echo $this->formRow($form->get(’title’));
?>
<div>
<?php echo $this->formInput($form->get(’submit’)); ?>
</div>
<?php
echo $this->form()->closeTag($form);
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 the title element, which has a label, we can use formRow() view helper which will
render the HTML for the label, the element and any validator messages that may exist. For the id and submit elements,
we use formHidden() and formInput() respectively as we only need to render the element itself. We also want
the submit button on its own line, so we put it within a div. Note that the formRow view helper is just a convenience -
we could have used formInput(), formLabel() and formElementErrors() separately had we wanted to.
If you now run the application from within Zend Studio and click the “Add new item” link from the task list page, you
should see:
You can now add a new task item and see it in the list of tasks.
59
Zend Framework 2 Documentation, Release 2.3.1dev
60 Chapter 17. Adding new tasks
CHAPTER 18
Editing a task
Editing a task is almost identical to adding one, so the code is very similar. This time we use editAction() in the
TaskController. Open TaskController.php and add this method to it:
module/Checklist/src/Checklist/Controller/TaskController.php:
public function editAction()
{
$id = (int)$this->params(’id’);
if (!$id) {
return $this->redirect()->toRoute(’task’, array(’action’=>’add’));
}
$task = $this->getTaskMapper()->getTask($id);
$form = new TaskForm();
$form->bind($task);
$request = $this->getRequest();
if ($request->isPost()) {
$form->setData($request->getPost());
if ($form->isValid()) {
$this->getTaskMapper()->saveTask($task);
return $this->redirect()->toRoute(’task’);
}
}
return array(
’id’ => $id,
’form’ => $form,
);
}
This code should look familiar. Let’s look at the only difference from adding a task: We look for the id that is in the
matched route and use it to load the task to be edited:
$id = (int)$this->params(’id’);
if (!$id) {
return $this->redirect()->toRoute(’task’, array(’action’=>’add’));
}
$task = $this->getTaskMapper()->getTask($id);
The params() method is a controller plugin that provides a convenient way to retrieve parameters from the
matched route. We use it to retrieve the id parameter that we defined in the task route that we created in the
61
Zend Framework 2 Documentation, Release 2.3.1dev
module.config.php. If the id is zero, then we redirect to the add action, otherwise, we continue by getting
the task entity from the database.
As we use the form’s bind() method with its hydrator, we do not need to populate the $task‘s data into the form
manually as it will automatically be transferred for us.
We also need to write a getTask() method in the TaskMapper to get a single record from the database, so let’s do
that now. Open TaskMapper.php and add this method:
module/Checklist/src/Checklist/Model/TaskMapper.php:
public function getTask($id)
{
$select = $this->sql->select();
$select->where(array(’id’ => $id));
$statement = $this->sql->prepareStatementForSqlObject($select);
$result = $statement->execute()->current();
if (!$result) {
return null;
}
$hydrator = new ClassMethods();
$task = new TaskEntity();
$hydrator->hydrate($result, $task);
return $task;
}
This method simply sets a where clause on the Sql‘s Select object and then executes it. Calling current() on
the result from execute() will return either the array of data for the row or false. If we retrieved data, then we
use the hydrator to populate a new TaskEntity ($task) with $data.
In the same way as with the action methods, the view template, edit.phtml, looks very similar to the one for adding
an task. Create a new PHP file called edit.phtml in in the module/Checklist/view/checklist/task
folder and add this code:
module/Checklist/view/checklist/task/edit.phtml:
<?php
$title = ’Edit task’;
$this->headTitle($title);
?>
<h1><?php echo $this->escapeHtml($title); ?></h1>
<?php
$form = $this->form;
$url = $this->url(’task’, array(’action’ => ’edit’, ’id’ => $id));
$form->setAttribute(’action’, $url);
$form->get(’submit’)->setAttribute(’value’, ’Edit’);
$form->prepare();
echo $this->form()->openTag($form);
echo $this->formHidden($form->get(’id’));
echo $this->formRow($form->get(’title’));
echo $this->formRow($form->get(’completed’));
?>
<div>
<?php echo $this->formInput($form->get(’submit’)); ?>
</div>
62 Chapter 18. Editing a task
Zend Framework 2 Documentation, Release 2.3.1dev
<?php
echo $this->form()->closeTag($form);
Compared to the add view script, we set the title to ‚’Edit Task’, and update the action URL to the edit action with the
correct id. We also change the label of the button to ‚’edit’ and render the completed form element.
You should now be able to edit tasks.
63
Zend Framework 2 Documentation, Release 2.3.1dev
64 Chapter 18. Editing a task
CHAPTER 19
Deleting a task
To round out the core functionality of our application, we need to be able to delete a task. We have a Delete link next
to each task on our list page and the naØve approach would be to run the delete action when it’s clicked. This would
be wrong. Remembering the HTTP specification, we recall that you shouldn’t do an irreversible action using GET and
should use POST instead.
We shall therefore 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, we’ll code it directly into our view (ZendForm is, after all, optional!).
Let’s start by adding the deleteAction() method to the TaskController. Open TaskController.php
and add this method to it:
module/Checklist/src/Checklist/Controller/TaskController.php:
public function deleteAction()
{
$id = $this->params(’id’);
$task = $this->getTaskMapper()->getTask($id);
if (!$task) {
return $this->redirect()->toRoute(’task’);
}
$request = $this->getRequest();
if ($request->isPost()) {
if ($request->getPost()->get(’del’) == ’Yes’) {
$this->getTaskMapper()->deleteTask($id);
}
return $this->redirect()->toRoute(’task’);
}
return array(
’id’ => $id,
’task’ => $task
);
}
As before, we get the id from the matched route and retrieve the task object. We then check the Request object’s
isPost() to determine whether to show the confirmation page or to delete the task. We use the TaskMapper‘s
deleteTask() method to delete the row and then redirect back to the list of tasks. If the request is not a POST,
then we assign the task to the view, along with the id.
We also need to write deleteTask(), so open TaskMapper.php and add this method:
module/Checklist/src/Checklist/Model/TaskMapper.php:
65
Zend Framework 2 Documentation, Release 2.3.1dev
public function deleteTask($id)
{
$delete = $this->sql->delete();
$delete->where(array(’id’ => $id));
$statement = $this->sql->prepareStatementForSqlObject($delete);
return $statement->execute();
}
This code should look fairly familiar as we again use a Delete object from ZendDbSql and execute the statement
from it. As we are using a Delete object, we set the where clause to avoid deleting every row in the table.
The view script is a simple HTML form. Create a new PHP file, delete.phtml in the
module/Checklist/view/checklist/task folder with this content:
module/Checklist/view/checklist/task/delete.phtml:
<?php
$title = ’Delete task’;
$this->headTitle($title);
?>
<h1><?php echo $this->escapeHtml($title); ?></h1>
<p>Are you sure that you want to delete the
’<?php echo $this->escapeHtml($task->title); ?>’ task?
</p>
<?php
$url = $this->url(’task’, array(’action’ => ’delete’, ’id’=>$id)); ?>
<form action="<?php echo $url; ?>" method="post">
<div>
<input type="submit" name="del" value="Yes" />
<input type="submit" name="del" value="No" />
</div>
</form>
In this view script, we display a confirmation message and then a form with just Yes and No buttons. In the action, we
checked specifically for the “Yes” value when doing the deletion.
That’s it - you now have a fully working application!
66 Chapter 19. Deleting a task
CHAPTER 20
Application Diagnostics
One really useful feature of Zend Server is the code trace feature that can show you the method-by-method execution
of any given PHP request. This is especially useful in a Zend Framework 2 application as the use of Events and Service
Manager means that our code base isn’t necessarily linear.
Let’s consider a contrived example and introduce a delay into our codebase. One of the more common causes of slow
down is related to database calls taking too long due to a complicated query, incorrect indexing or by retrieving too
much data. We have a very simple database table with just 5 rows, so we can simulate this by adding a sleep() call
to our TaskMapper‘s fetchAll() method.
Open Checklist/src/Checklist/Model/TaskMapper.php and add sleep(5); just before the end of
the fetchAll() method:
Checklist/src/Checklist/Model/TaskMapper.php:
public function fetchAll()
{
$select = $this->sql->select();
$select->order(array(’completed ASC’, ’created ASC’));
$statement = $this->sql->prepareStatementForSqlObject($select);
$results = $statement->execute();
$entityPrototype = new TaskEntity();
$hydrator = new ClassMethods();
$resultset = new HydratingResultSet($hydrator, $entityPrototype);
$resultset->initialize($results);
sleep(5);
return $resultset;
}
It will now take 5 seconds (and a little bit) to display the list of tasks.
If you now look at the home page of Zend Server’s console, you’ll see a “Slow Request Execution” critical event listed.
Click on the “show” link in the “Code Trace” column as shown:
You will then see much more detail about this critical event. The easiest way to use the profile view is to click on the
“Statistics per Function” tab and then order by “Just own” total running time.
This will result in the display of the slowest method at the top as shown in the sceenshot.
As you can see, Zend Server has correctly determined that fetchAll() is the cause of the slowdown and so we can
immediately go to the problem source in Zend Studio and fix the problem.
67
Zend Framework 2 Documentation, Release 2.3.1dev
In addition to helping debugging while developing, this is obviously also extremely powerful when Zend Server is
running on the production servers as this profile information is then available for those situations when a given issue
only seems to happen on the live web site.
68 Chapter 20. Application Diagnostics
CHAPTER 21
Step-by-step debugging
Another useful feature of Zend Studio and Eclipse/PDT is the step-by-step debugger. With the debugger you can set
breakpoints in your code and then run the page in a browser. When the breakpoint is reached, Zend Studio pauses the
page and you can then inspect variables and move forward through your code one line at a time.
To see this in action, let’s inspect the value of $task in the checklist module’s index.phtml file. Open the mod-
ule/Checklist/view/checklist/task/index.phtml file and double click in the gutter next to the opening <a tag to set a
blue breakpoint marker:
The break point is now set. The easiest way to run to this point is to use the Zend Studio Firefox tool bar. Once in-
stalled, you can navigate to http://localhost:10088/MyTaskList/public/task in Firefox and then press the Debug button
in the toolbar. Zend Studio will then come to the foreground and ask you if you want to use the Debug perspective.
Answer yes, as this view is designed to provide useful information while debugging. Zend Studio will pause the
application on the first line of index.php, so press F8 to continue to the breakpoint that you set.
You will now see the code we are interested in. The centre pane shows our code with the line that the debugger is
stopped on highlighted. The top left pane shows the stack trace which tells us which methods were used to get to this
line of code. The top right pane shows a list of variables in scope. You can click the arrow next to $task to expand
it and see the properties of the object. Pressing F8 will resume running the application until the next breakpoint. As
our breakpoint is in a loop, it iterates once around the loop and stops again. The data in $task is now the second
database record. Once you have finished inspecting the state of your code, you can press the square red stop button to
stop the debugging mode. Clicking the PHP button in the top right hand corner of Zend Studio takes you back to the
code editing view.
69
Zend Framework 2 Documentation, Release 2.3.1dev
70 Chapter 21. Step-by-step debugging
CHAPTER 22
Conclusion
This concludes our brief look at building a simple, but fully functional, Zend Framework 2 application using Zend
Studio 10 with the code running on Zend Server 6. It barely scratches the surface of the power and flexibility of Zend
Framework and we recommend reading the manual for more information. Similarly, the combination of Zend Studio
and Zend Server makes for a very powerful system for writing, debugging and deploying PHP applications. The Zend
Studio manual is very helpful for getting the most out of this tool.
71
Zend Framework 2 Documentation, Release 2.3.1dev
72 Chapter 22. Conclusion
CHAPTER 23
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.
23.1 Installation using Composer
1. Open console (command prompt)
2. Go to your application’s directory
3. Run composer require zendframework/zftool:dev-master
23.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.
23.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.
73
Zend Framework 2 Documentation, Release 2.3.1dev
23.4 Usage
23.4.1 From Composer or Manual install:
The zf.php should be installed into the vendor/ZFTool directory (relative to your project root) - however, the command
needs to be run from your project root in order for it to work correctly. You can symlink vendor/ZFTool/zf.php to your
project root, or alternatively substitute zf.php for vendor/ZFTool/zf.php in the examples below.
23.4.2 Using the PHAR:
Simply substitute zftool.phar for zf.php in the below examples.
23.4.3 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.
23.4.4 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.
23.4.5 Module creation
> zf.php create module <name> [<path>]
<name> The name of the module to be created
<path> 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).
23.4.6 Classmap generator
> zf.php classmap generate <directory> <classmap file> [--append|-a] [--overwrite|-w]
<directory> The directory to scan for PHP classes (use "." to use current directory)
<classmap file> File name for generated class map file or - for standard output. If not supplied
autoload_classmap.php inside <directory>.
--append | -a Append to classmap file if it exists
--overwrite | -w Whether or not to overwrite existing classmap file
74 Chapter 23. Zend Framework Tool (ZFTool)
Zend Framework 2 Documentation, Release 2.3.1dev
23.4.7 ZF library installation
> zf.php install zf <path> [<version>]
<path> The directory where to install the ZF2 library
<version> The version to install, if not specified uses the last available
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).
23.4.8 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.
23.4. Usage 75
Zend Framework 2 Documentation, Release 2.3.1dev
76 Chapter 23. Zend Framework Tool (ZFTool)
CHAPTER 24
Learning Dependency Injection
24.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.
24.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
3 class A
4 {
5 /* Some useful functionality */
6 }
7
8 class B
9 {
10 protected $a = null;
11 public function __construct(A $a)
12 {
13 $this->a = $a;
14 }
15 }
16 }
To create B by hand, a developer would follow this work flow, or a similar workflow to this:
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
77
Zend Framework 2 Documentation, Release 2.3.1dev
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 $di = new ZendDiDi;
2 $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
3 class A
4 {
5 protected $username = null;
6 protected $password = null;
7 public function __construct($username, $password)
8 {
9 $this->username = $username;
10 $this->password = $password;
11 }
12 }
13
14 class B
15 {
16 protected $a = null;
17 public function __construct(A $a)
18 {
19 $this->a = $a;
20 }
21 }
22
23 class C
24 {
25 protected $b = null;
26 public function __construct(B $b)
27 {
28 $this->b = $b;
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 $di = new ZendDiDi;
2 $di->getInstanceManager()->setProperty(’A’, ’username’, ’MyUsernameValue’);
3 $di->getInstanceManager()->setProperty(’A’, ’password’, ’MyHardToGuessPassword%$#’);
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:
78 Chapter 24. Learning Dependency Injection
Zend Framework 2 Documentation, Release 2.3.1dev
1 $c = $di->get(’MyC’);
2 // or
3 $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 $parameters = array(
2 ’username’ => ’MyUsernameValue’,
3 ’password’ => ’MyHardToGuessPassword%$#’,
4 );
5
6 $c = $di->get(’MyC’, $parameters);
7 // or
8 $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 namespace My {
2 class B
3 {
4 protected $a;
5 public function setA(A $a)
6 {
7 $this->a = $a;
8 }
9 }
10 }
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.
24.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
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:
24.3. Simplest Usage Case Without Type-hints 79
Zend Framework 2 Documentation, Release 2.3.1dev
1 namespace My {
2 class B
3 {
4 protected $a;
5 public function setA($a)
6 {
7 $this->a = $a;
8 }
9 }
10 }
You’ll notice the only change is that setA now does not include any type-hinting information.
1 use ZendDiDi;
2 use ZendDiDefinition;
3 use ZendDiDefinitionBuilder;
4
5 // Describe this class:
6 $builder = new DefinitionBuilderDefinition;
7 $builder->addClass(($class = new BuilderPhpClass));
8
9 $class->setName(’MyB’);
10 $class->addInjectableMethod(($im = new BuilderInjectableMethod));
11
12 $im->setName(’setA’);
13 $im->addParameter(’a’, ’MyA’);
14
15 // Use both our Builder Definition as well as the default
16 // RuntimeDefinition, builder first
17 $aDef = new DefinitionAggregateDefinition;
18 $aDef->addDefinition($builder);
19 $aDef->addDefinition(new DefinitionRuntimeDefinition);
20
21 // Now make sure the Di understands it
22 $di = new Di;
23 $di->setDefinition($aDef);
24
25 // and finally, create C
26 $parameters = array(
27 ’username’ => ’MyUsernameValue’,
28 ’password’ => ’MyHardToGuessPassword%$#’,
29 );
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.
24.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
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
80 Chapter 24. Learning Dependency Injection
Zend Framework 2 Documentation, Release 2.3.1dev
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 $compiler = new ZendDiDefinitionCompiler();
2 $compiler->addCodeScannerDirectory(
3 new ZendCodeScannerScannerDirectory(’path/to/library/My/’)
4 );
5 $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 $di = new ZendDiDi;
2 $di->setDefinition($definition);
3 $di->getInstanceManager()->setProperty(’MyA’, ’username’, ’foo’);
4 $di->getInstanceManager()->setProperty(’MyA’, ’password’, ’bar’);
5 $c = $di->get(’MyC’);
One strategy for persisting these compiled definitions would be the following:
1 if (!file_exists(__DIR__ . ’/di-definition.php’) && $isProduction) {
2 $compiler = new ZendDiDefinitionCompiler();
3 $compiler->addCodeScannerDirectory(
4 new ZendCodeScannerScannerDirectory(’path/to/library/My/’)
5 );
6 $definition = $compiler->compile();
7 file_put_contents(
8 __DIR__ . ’/di-definition.php’,
9 ’<?php return ’ . var_export($definition->toArray(), true) . ’;’
10 );
11 } else {
12 $definition = new ZendDiDefinitionArrayDefinition(
13 include __DIR__ . ’/di-definition.php’
14 );
15 }
16
24.4. Simplest usage case with Compiled Definition 81
Zend Framework 2 Documentation, Release 2.3.1dev
17 // $definition can now be used; in a production system it will be written
18 // to disk.
Since ZendCodeScanner does not include files, the classes contained within are not loaded into memory. In-
stead, 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.
24.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 // First, compile the information
2 $compiler = new ZendDiDefinitionCompilerDefinition();
3 $compiler->addDirectoryScanner(
4 new ZendCodeScannerDirectoryScanner(__DIR__ . ’/My/’)
5 );
6 $compiler->compile();
7 $definition = $compiler->toArrayDefinition();
8
9 // Now, create a Definition class for this information
10 $codeGenerator = new ZendCodeGeneratorFileGenerator();
11 $codeGenerator->setClass(($class = new ZendCodeGeneratorClassGenerator()));
12 $class->setNamespaceName(’My’);
13 $class->setName(’DiDefinition’);
14 $class->setExtendedClass(’ZendDiDefinitionArrayDefinition’);
15 $class->addMethod(
16 ’__construct’,
17 array(),
18 ZendCodeGeneratorMethodGenerator::FLAG_PUBLIC,
19 ’parent::__construct(’ . var_export($definition->toArray(), true) . ’);’
20 );
21 file_put_contents(__DIR__ . ’/My/DiDefinition.php’, $codeGenerator->generate());
24.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 use ZendDiDi;
2 use ZendDiDefinition;
3 use ZendDiDefinitionBuilder;
4
5 $di = new Di;
6 $diDefAggregate = new DefinitionAggregate();
7
8 // first add in provided Definitions, for example
9 $diDefAggregate->addDefinition(new ThirdPartyDbalDiDefinition());
10 $diDefAggregate->addDefinition(new ZendControllerDiDefinition());
11
82 Chapter 24. Learning Dependency Injection
Zend Framework 2 Documentation, Release 2.3.1dev
12 // for code that does not have TypeHints
13 $builder = new DefinitionBuilderDefinition();
14 $builder->addClass(($class = BuilderPhpClass));
15 $class->addInjectionMethod(
16 ($injectMethod = new BuilderInjectionMethod())
17 );
18 $injectMethod->setName(’injectImplementation’);
19 $injectMethod->addParameter(
20 ’implementation’, ’ClassForSpecificImplementation’
21 );
22
23 // now, your application code
24 $compiler = new DefinitionCompiler()
25 $compiler->addCodeScannerDirectory(
26 new ZendCodeScannerDirectoryScanner(__DIR__ . ’/App/’)
27 );
28 $appDefinition = $compiler->compile();
29 $diDefAggregate->addDefinition($appDefinition);
30
31 // now, pass in properties
32 $im = $di->getInstanceManager();
33
34 // this could come from ZendConfigConfig::toArray
35 $propertiesFromConfig = array(
36 ’ThirdPartyDbalDbAdapter’ => array(
37 ’username’ => ’someUsername’,
38 ’password’ => ’somePassword’
39 ),
40 ’ZendControllerHelperContentType’ => array(
41 ’default’ => ’xhtml5’
42 ),
43 );
44 $im->setProperties($propertiesFromConfig);
24.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 // $di is a fully configured DI instance
4 $generator = new Generator($di);
5
6 $generator->setNamespace(’Application’)
24.7. Generating Service Locators 83
Zend Framework 2 Documentation, Release 2.3.1dev
7 ->setContainerClass(’Context’);
8 $file = $generator->getCodeGenerator();
9 $file->setFilename(__DIR__ . ’/../Application/Context.php’);
10 $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 class Context extends ServiceLocator
8 {
9
10 public function get($name, array $params = array())
11 {
12 switch ($name) {
13 case ’composed’:
14 case ’MyComposedClass’:
15 return $this->getMyComposedClass();
16
17 case ’struct’:
18 case ’MyStruct’:
19 return $this->getMyStruct();
20
21 default:
22 return parent::get($name, $params);
23 }
24 }
25
26 public function getComposedClass()
27 {
28 if (isset($this->services[’MyComposedClass’])) {
29 return $this->services[’MyComposedClass’];
30 }
31
32 $object = new MyComposedClass();
33 $this->services[’MyComposedClass’] = $object;
34 return $object;
35 }
36 public function getMyStruct()
37 {
38 if (isset($this->services[’MyStruct’])) {
39 return $this->services[’MyStruct’];
40 }
41
42 $object = new MyStruct();
43 $this->services[’MyStruct’] = $object;
44 return $object;
45 }
46
47 public function getComposed()
48 {
49 return $this->get(’MyComposedClass’);
50 }
84 Chapter 24. Learning Dependency Injection
Zend Framework 2 Documentation, Release 2.3.1dev
51
52 public function getStruct()
53 {
54 return $this->get(’MyStruct’);
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.
24.7. Generating Service Locators 85
Zend Framework 2 Documentation, Release 2.3.1dev
86 Chapter 24. Learning Dependency Injection
CHAPTER 25
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 in-
volved. Going back and manually testing every individual component of an application after every change is imprac-
tical. 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.*
25.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.
25.2 Bootstrapping your tests
Next, create a file called phpunit.xml under zf2-tutorial/module/Album/test:
87
Zend Framework 2 Documentation, Release 2.3.1dev
<?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 use ZendLoaderAutoloaderFactory;
6 use ZendMvcServiceServiceManagerConfig;
7 use ZendServiceManagerServiceManager;
8 use RuntimeException;
9
10 error_reporting(E_ALL | E_STRICT);
11 chdir(__DIR__);
12
13 /**
14 * Test bootstrap, for setting up autoloading
15 */
16 class Bootstrap
17 {
18 protected static $serviceManager;
19
20 public static function init()
21 {
22 $zf2ModulePaths = array(dirname(dirname(__DIR__)));
23 if (($path = static::findParentPath(’vendor’))) {
24 $zf2ModulePaths[] = $path;
25 }
26 if (($path = static::findParentPath(’module’)) !== $zf2ModulePaths[0]) {
27 $zf2ModulePaths[] = $path;
28 }
29
30 static::initAutoloader();
31
32 // use ModuleManager to load this module and it’s dependencies
33 $config = array(
34 ’module_listener_options’ => array(
35 ’module_paths’ => $zf2ModulePaths,
36 ),
37 ’modules’ => array(
38 ’Album’
39 )
40 );
41
42 $serviceManager = new ServiceManager(new ServiceManagerConfig());
43 $serviceManager->setService(’ApplicationConfig’, $config);
44 $serviceManager->get(’ModuleManager’)->loadModules();
45 static::$serviceManager = $serviceManager;
46 }
88 Chapter 25. Unit Testing a Zend Framework 2 application
Zend Framework 2 Documentation, Release 2.3.1dev
47
48 public static function chroot()
49 {
50 $rootPath = dirname(static::findParentPath(’module’));
51 chdir($rootPath);
52 }
53
54 public static function getServiceManager()
55 {
56 return static::$serviceManager;
57 }
58
59 protected static function initAutoloader()
60 {
61 $vendorPath = static::findParentPath(’vendor’);
62
63 $zf2Path = getenv(’ZF2_PATH’);
64 if (!$zf2Path) {
65 if (defined(’ZF2_PATH’)) {
66 $zf2Path = ZF2_PATH;
67 } elseif (is_dir($vendorPath . ’/ZF2/library’)) {
68 $zf2Path = $vendorPath . ’/ZF2/library’;
69 } elseif (is_dir($vendorPath . ’/zendframework/zendframework/library’)) {
70 $zf2Path = $vendorPath . ’/zendframework/zendframework/library’;
71 }
72 }
73
74 if (!$zf2Path) {
75 throw new RuntimeException(
76 ’Unable to load ZF2. Run ‘php composer.phar install‘ or’
77 . ’ define a ZF2_PATH environment variable.’
78 );
79 }
80
81 if (file_exists($vendorPath . ’/autoload.php’)) {
82 include $vendorPath . ’/autoload.php’;
83 }
84
85 include $zf2Path . ’/Zend/Loader/AutoloaderFactory.php’;
86 AutoloaderFactory::factory(array(
87 ’ZendLoaderStandardAutoloader’ => array(
88 ’autoregister_zf’ => true,
89 ’namespaces’ => array(
90 __NAMESPACE__ => __DIR__ . ’/’ . __NAMESPACE__,
91 ),
92 ),
93 ));
94 }
95
96 protected static function findParentPath($path)
97 {
98 $dir = __DIR__;
99 $previousDir = ’.’;
100 while (!is_dir($dir . ’/’ . $path)) {
101 $dir = dirname($dir);
102 if ($previousDir === $dir) {
103 return false;
104 }
25.2. Bootstrapping your tests 89
Zend Framework 2 Documentation, Release 2.3.1dev
105 $previousDir = $dir;
106 }
107 return $dir . ’/’ . $path;
108 }
109 }
110
111 Bootstrap::init();
112 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.
25.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 AlbumControllerTest.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();
}
}
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.
90 Chapter 25. Unit Testing a Zend Framework 2 application
Zend Framework 2 Documentation, Release 2.3.1dev
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.
25.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;
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.
25.4. A failing test case 91
Zend Framework 2 Documentation, Release 2.3.1dev
25.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 fol-
lows:
1 public function testIndexActionCanBeAccessed()
2 {
3 $albumTableMock = $this->getMockBuilder(’AlbumModelAlbumTable’)
4 ->disableOriginalConstructor()
5 ->getMock();
6
7 $albumTableMock->expects($this->once())
8 ->method(’fetchAll’)
9 ->will($this->returnValue(array()));
10
11 $serviceManager = $this->getApplicationServiceLocator();
12 $serviceManager->setAllowOverride(true);
13 $serviceManager->setService(’AlbumModelAlbumTable’, $albumTableMock);
14
15 $this->dispatch(’/album’);
16 $this->assertResponseStatusCode(200);
17
18 $this->assertModuleName(’Album’);
19 $this->assertControllerName(’AlbumControllerAlbum’);
20 $this->assertControllerClass(’AlbumController’);
21 $this->assertMatchedRouteName(’album’);
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
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
.
92 Chapter 25. Unit Testing a Zend Framework 2 application
Zend Framework 2 Documentation, Release 2.3.1dev
Time: 0 seconds, Memory: 9.00Mb
OK (1 test, 6 assertions)
25.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
..
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.
When testing the editAction you will also need to mock out the getAlbum method:
$albumTableMock->expects($this->once())
->method(’getAlbum’)
->will($this->returnValue(new AlbumModelAlbum()));
25.6. Testing actions with POST 93
Zend Framework 2 Documentation, Release 2.3.1dev
25.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 <?php
2 namespace AlbumTestModel;
3
4 use AlbumModelAlbum;
5 use PHPUnit_Framework_TestCase;
6
7 class AlbumTest extends PHPUnit_Framework_TestCase
8 {
9 public function testAlbumInitialState()
10 {
11 $album = new Album();
12
13 $this->assertNull(
14 $album->artist,
15 ’"artist" should initially be null’
16 );
17 $this->assertNull(
18 $album->id,
19 ’"id" should initially be null’
20 );
21 $this->assertNull(
22 $album->title,
23 ’"title" should initially be null’
24 );
25 }
26
27 public function testExchangeArraySetsPropertiesCorrectly()
28 {
29 $album = new Album();
30 $data = array(’artist’ => ’some artist’,
31 ’id’ => 123,
32 ’title’ => ’some title’);
33
34 $album->exchangeArray($data);
35
36 $this->assertSame(
37 $data[’artist’],
38 $album->artist,
39 ’"artist" was not set correctly’
40 );
41 $this->assertSame(
42 $data[’id’],
43 $album->id,
44 ’"id" was not set correctly’
45 );
46 $this->assertSame(
47 $data[’title’],
94 Chapter 25. Unit Testing a Zend Framework 2 application
Zend Framework 2 Documentation, Release 2.3.1dev
48 $album->title,
49 ’"title" was not set correctly’
50 );
51 }
52
53 public function testExchangeArraySetsPropertiesToNullIfKeysAreNotPresent()
54 {
55 $album = new Album();
56
57 $album->exchangeArray(array(’artist’ => ’some artist’,
58 ’id’ => 123,
59 ’title’ => ’some title’));
60 $album->exchangeArray(array());
61
62 $this->assertNull(
63 $album->artist, ’"artist" should have defaulted to null’
64 );
65 $this->assertNull(
66 $album->id, ’"id" should have defaulted to null’
67 );
68 $this->assertNull(
69 $album->title, ’"title" should have defaulted to null’
70 );
71 }
72
73 public function testGetArrayCopyReturnsAnArrayWithPropertyValues()
74 {
75 $album = new Album();
76 $data = array(’artist’ => ’some artist’,
77 ’id’ => 123,
78 ’title’ => ’some title’);
79
80 $album->exchangeArray($data);
81 $copyArray = $album->getArrayCopy();
82
83 $this->assertSame(
84 $data[’artist’],
85 $copyArray[’artist’],
86 ’"artist" was not set correctly’
87 );
88 $this->assertSame(
89 $data[’id’],
90 $copyArray[’id’],
91 ’"id" was not set correctly’
92 );
93 $this->assertSame(
94 $data[’title’],
95 $copyArray[’title’],
96 ’"title" was not set correctly’
97 );
98 }
99
100 public function testInputFiltersAreSetCorrectly()
101 {
102 $album = new Album();
103
104 $inputFilter = $album->getInputFilter();
105
25.7. Testing model entities 95
Zend Framework 2 Documentation, Release 2.3.1dev
106 $this->assertSame(3, $inputFilter->count());
107 $this->assertTrue($inputFilter->has(’artist’));
108 $this->assertTrue($inputFilter->has(’id’));
109 $this->assertTrue($inputFilter->has(’title’));
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)
25.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 con-
tents:
<?php
namespace AlbumTestModel;
use AlbumModelAlbumTable;
use AlbumModelAlbum;
use ZendDbResultSetResultSet;
use PHPUnit_Framework_TestCase;
class AlbumTableTest extends PHPUnit_Framework_TestCase
{
public function testFetchAllReturnsAllAlbums()
{
$resultSet = new ResultSet();
$mockTableGateway = $this->getMock(
’ZendDbTableGatewayTableGateway’,
array(’select’),
96 Chapter 25. Unit Testing a Zend Framework 2 application
Zend Framework 2 Documentation, Release 2.3.1dev
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));
}
public function testCanDeleteAnAlbumByItsId()
{
$mockTableGateway = $this->getMock(
’ZendDbTableGatewayTableGateway’,
array(’delete’),
array(),
’’,
false
25.8. Testing model tables 97
Zend Framework 2 Documentation, Release 2.3.1dev
);
$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’),
array(),
’’,
false
);
$mockTableGateway->expects($this->once())
->method(’select’)
->with(array(’id’ => 123))
->will($this->returnValue($resultSet));
98 Chapter 25. Unit Testing a Zend Framework 2 application
Zend Framework 2 Documentation, Release 2.3.1dev
$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.
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:
25.8. Testing model tables 99
Zend Framework 2 Documentation, Release 2.3.1dev
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)
25.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.
100 Chapter 25. Unit Testing a Zend Framework 2 application
CHAPTER 26
Using the EventManager
This tutorial explores the various features of ZendEventManager.
26.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.
26.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 $events = new EventManager();
4 $events->attach(’do’, function ($e) {
5 $event = $e->getName();
6 $params = $e->getParams();
7 printf(
8 ’Handled event "%s", with parameters %s’,
9 $event,
10 json_encode($params)
11 );
12 });
13
14 $params = array(’foo’ => ’bar’, ’baz’ => ’bat’);
15 $events->trigger(’do’, null, $params);
101
Zend Framework 2 Documentation, Release 2.3.1dev
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 use ZendEventManagerEventManager;
2 use ZendEventManagerEventManagerAwareInterface;
3 use ZendEventManagerEventManagerInterface;
4
5 class Example implements EventManagerAwareInterface
6 {
7 protected $events;
8
9 public function setEventManager(EventManagerInterface $events)
10 {
11 $events->setIdentifiers(array(
12 __CLASS__,
13 get_class($this)
14 ));
15 $this->events = $events;
16 }
17
18 public function getEventManager()
19 {
20 if (!$this->events) {
21 $this->setEventManager(new EventManager());
22 }
23 return $this->events;
24 }
25
26 public function do($foo, $baz)
27 {
28 $params = compact(’foo’, ’baz’);
29 $this->getEventManager()->trigger(__FUNCTION__, $this, $params);
30 }
31
32 }
33
34 $example = new Example();
35
36 $example->getEventManager()->attach(’do’, function($e) {
37 $event = $e->getName();
38 $target = get_class($e->getTarget()); // "Example"
39 $params = $e->getParams();
40 printf(
41 ’Handled event "%s" on target "%s", with parameters %s’,
42 $event,
43 $target,
44 json_encode($params)
102 Chapter 26. Using the EventManager
Zend Framework 2 Documentation, Release 2.3.1dev
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 ar-
gument 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?
26.3 Shared managers
One aspect that the EventManager implementation provides is an ability to compose a
SharedEventManagerInterface implementation.
ZendEventManagerSharedEventManagerInterface describes an object that aggregates listeners for
events attached to objects with specific identifiers. It does not trigger events itself. Instead, an EventManager in-
stance 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 $sharedEvents = new SharedEventManager();
4 $sharedEvents->attach(’Example’, ’do’, function ($e) {
5 $event = $e->getName();
6 $target = get_class($e->getTarget()); // "Example"
7 $params = $e->getParams();
8 printf(
9 ’Handled event "%s" on target "%s", with parameters %s’,
10 $event,
11 $target,
12 json_encode($params)
13 );
14 });
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 $example = new Example();
2 $example->getEventManager()->setSharedManager($sharedEvents);
3 $example->do(’bar’, ’bat’);
and expect the following to be echo‘d:
26.3. Shared managers 103
Zend Framework 2 Documentation, Release 2.3.1dev
Handled event "do" on target "Example", with parameters {"foo":"bar","baz":"bat"}
Now, let’s say we extended Example as follows:
1 class SubExample extends Example
2 {
3 }
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);
104 Chapter 26. Using the EventManager
CHAPTER 27
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 // Multiple named events:
2 $events->attach(
3 array(’foo’, ’bar’, ’baz’), // events
4 $listener
5 );
6
7 // All events via wildcard:
8 $events->attach(
9 ’*’, // all events
10 $listener
11 );
12
13 // Multiple named targets:
14 $sharedEvents->attach(
15 array(’Foo’, ’Bar’, ’Baz’), // targets
16 ’doSomething’, // named event
17 $listener
18 );
19
20 // All targets via wildcard
21 $sharedEvents->attach(
22 ’*’, // all targets
23 ’doSomething’, // named event
24 $listener
25 );
26
27 // Mix and match: multiple named events on multiple named targets:
28 $sharedEvents->attach(
29 array(’Foo’, ’Bar’, ’Baz’), // targets
30 array(’foo’, ’bar’, ’baz’), // events
31 $listener
32 );
33
34 // Mix and match: all events on multiple named targets:
35 $sharedEvents->attach(
105
Zend Framework 2 Documentation, Release 2.3.1dev
36 array(’Foo’, ’Bar’, ’Baz’), // targets
37 ’*’, // events
38 $listener
39 );
40
41 // Mix and match: multiple named events on all targets:
42 $sharedEvents->attach(
43 ’*’, // targets
44 array(’foo’, ’bar’, ’baz’), // events
45 $listener
46 );
47
48 // Mix and match: all events on all targets:
49 $sharedEvents->attach(
50 ’*’, // targets
51 ’*’, // events
52 $listener
53 );
The ability to specify multiple targets and/or events when attaching can slim down your code immensely.
106 Chapter 27. Wildcards
CHAPTER 28
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 mul-
tiple 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 use ZendEventManagerEventInterface;
2 use ZendEventManagerEventManagerInterface;
3 use ZendEventManagerListenerAggregateInterface;
4 use ZendLogLogger;
5
6 class LogEvents implements ListenerAggregateInterface
7 {
8 protected $listeners = array();
9 protected $log;
10
11 public function __construct(Logger $log)
12 {
13 $this->log = $log;
14 }
15
16 public function attach(EventManagerInterface $events)
17 {
18 $this->listeners[] = $events->attach(’do’, array($this, ’log’));
19 $this->listeners[] = $events->attach(’doSomethingElse’, array($this, ’log’));
20 }
21
22 public function detach(EventCollection $events)
23 {
24 foreach ($this->listeners as $index => $listener) {
25 if ($events->detach($listener)) {
26 unset($this->listeners[$index];
27 }
28 }
29 }
30
31 public function log(EventInterface $e)
32 {
33 $event = $e->getName();
107
Zend Framework 2 Documentation, Release 2.3.1dev
34 $params = $e->getParams();
35 $this->log->info(sprintf(’%s: %s’, $event, json_encode($params)));
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.
28.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 in-
stance. 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.
28.2 Short-circuiting 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.
Here’s an example:
108 Chapter 28. Listener aggregates
Zend Framework 2 Documentation, Release 2.3.1dev
1 public function someExpensiveCall($criteria1, $criteria2)
2 {
3 $params = compact(’criteria1’, ’criteria2’);
4 $results = $this->getEventManager()->trigger(
5 __FUNCTION__,
6 $this,
7 $params,
8 function ($r) {
9 return ($r instanceof SomeResultClass);
10 }
11 );
12 if ($results->stopped()) {
13 return $results->last();
14 }
15
16 // ... do some work ...
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 $events->attach(’do’, function ($e) {
2 $e->stopPropagation();
3 return new SomeResultClass();
4 });
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.
28.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 SharedEventManager::attach() accept one additional argu-
ment, 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 $priority = 100;
2 $events->attach(’Example’, ’do’, function($e) {
3 $event = $e->getName();
4 $target = get_class($e->getTarget()); // "Example"
5 $params = $e->getParams();
6 printf(
7 ’Handled event "%s" on target "%s", with parameters %s’,
8 $event,
9 $target,
10 json_encode($params)
28.3. Keeping it in order 109
Zend Framework 2 Documentation, Release 2.3.1dev
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 neces-
sary in order to set appropriate priority values. We advise avoiding setting a priority value unless absolutely necessary.
28.4 Custom event objects
Hopefully some of you have been wondering, “where and when is the Event object created”? In all of the exam-
ples 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 $routeMatch = $e->getParam(’route-match’, false);
2 if (!$routeMatch) {
3 // Oh noes! we cannot do our work! whatever shall we do?!?!?!
4 }
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 // in the method:
2 $params[’__RESULT’] = $computedResult;
3 $events->trigger(__FUNCTION__ . ’.post’, $this, $params);
4
5 // in the listener:
6 $result = $e->getParam(’__RESULT__’);
7 if (!$result) {
8 // Oh noes! we cannot do our work! whatever shall we do?!?!?!
9 }
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 $response = $e->getResponse();
2 $result = $e->getResult();
3 if (is_string($result)) {
4 $content = $view->render(’layout.phtml’, array(’content’ => $result));
5 $response->setContent($content);
6 }
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.
110 Chapter 28. Listener aggregates
Zend Framework 2 Documentation, Release 2.3.1dev
1 $event = new CustomEvent();
2 $event->setSomeKey($value);
3
4 // Injected with event name and target:
5 $events->trigger(’foo’, $this, $event);
6
7 // Injected with event name:
8 $event->setTarget($this);
9 $events->trigger(’foo’, $event);
10
11 // Fully encapsulates all necessary properties:
12 $event->setName(’foo’);
13 $event->setTarget($this);
14 $events->trigger($event);
15
16 // Passing a callback following the event object works for
17 // short-circuiting, too.
18 $results = $events->trigger(’foo’, $this, $event, $callback);
This is a really powerful technique for domain-specific event systems, and definitely worth experimenting with.
28.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 public function someExpensiveCall($criteria1, $criteria2)
2 {
3 $params = compact(’criteria1’, ’criteria2’);
4 $results = $this->getEventManager()->trigger(
5 __FUNCTION__ . ’.pre’,
6 $this,
7 $params,
8 function ($r) {
9 return ($r instanceof SomeResultClass);
10 }
11 );
12 if ($results->stopped()) {
13 return $results->last();
14 }
15
16 // ... do some work ...
28.5. Putting it together: Implementing a simple caching system 111
Zend Framework 2 Documentation, Release 2.3.1dev
17
18 $params[’__RESULT__’] = $calculatedResult;
19 $this->events()->trigger(__FUNCTION__ . ’.post’, $this, $params);
20 return $calculatedResult;
21 }
Now, to provide some caching listeners. We’ll need to attach to each of the “someExpensiveCall.pre” and “someEx-
pensiveCall.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 $events->attach(’someExpensiveCall.pre’, function($e) use ($cache) {
2 $params = $e->getParams();
3 $key = md5(json_encode($params));
4 $hit = $cache->load($key);
5 return $hit;
6 }, 100);
7
8 $events->attach(’someExpensiveCall.post’, function($e) use ($cache) {
9 $params = $e->getParams();
10 $result = $params[’__RESULT__’];
11 unset($params[’__RESULT__’]);
12 $key = md5(json_encode($params));
13 $cache->save($result, $key);
14 }, -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 public function setEventManager(EventManagerInterface $events)
2 {
3 $this->events = $events;
4 $events->setIdentifiers(array(__CLASS__, get_class($this)));
5 $events->attach(’someExpensiveCall’, array($this, ’doSomeExpensiveCall’));
6 }
7
8 public function someExpensiveCall($criteria1, $criteria2)
9 {
10 $params = compact(’criteria1’, ’criteria2’);
11 $results = $this->getEventManager()->trigger(
12 __FUNCTION__,
13 $this,
14 $params,
15 function ($r) {
16 return ($r instanceof SomeResultClass);
17 }
18 );
19 return $results->last();
20 }
21
22 public function doSomeExpensiveCall($e)
112 Chapter 28. Listener aggregates
Zend Framework 2 Documentation, Release 2.3.1dev
23 {
24 // ... do some work ...
25 $e->setParam(’__RESULT__’, $calculatedResult);
26 return $calculatedResult;
27 }
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.
28.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.
28.6. Conclusion 113
Zend Framework 2 Documentation, Release 2.3.1dev
114 Chapter 28. Listener aggregates
CHAPTER 29
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 con-
figuration globbed in specified directories (typically config/autoload/).
• Finally, immediately prior to the merged application configuration being passed to the ServiceManager, it
is passed to a special EVENT_MERGE_CONFIG event to allow further modification.
In this tutorial, we’ll look at the exact sequence, and how you can tie into it.
29.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 configura-
tion 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 is by default in
config/application.config.php. The defaults look like this:
1 <?php
2 return array(
3 // This should be an array of module namespaces used in the application.
4 ’modules’ => array(
5 ’Application’,
6 ),
7
8 // These are various options for the listeners attached to the ModuleManager
9 ’module_listener_options’ => array(
10 // This should be an array of paths in which modules reside.
11 // If a string key is provided, the listener will consider that a module
12 // namespace, the value of that key the specific path to that module’s
13 // Module class.
14 ’module_paths’ => array(
15 ’./module’,
16 ’./vendor’,
115
Zend Framework 2 Documentation, Release 2.3.1dev
17 ),
18
19 // An array of paths from which to glob configuration files after
20 // modules are loaded. These effectively overide configuration
21 // provided by modules themselves. Paths may use GLOB_BRACE notation.
22 ’config_glob_paths’ => array(
23 ’config/autoload/{,*.}{global,local}.php’,
24 ),
25
26 // Whether or not to enable a configuration cache.
27 // If enabled, the merged configuration will be cached and used in
28 // subsequent requests.
29 //’config_cache_enabled’ => $booleanValue,
30
31 // The key used to create the configuration cache file name.
32 //’config_cache_key’ => $stringKey,
33
34 // Whether or not to enable a module class map cache.
35 // If enabled, creates a module class map cache which will be used
36 // by in future requests, to reduce the autoloading process.
37 //’module_map_cache_enabled’ => $booleanValue,
38
39 // The key used to create the class map cache file name.
40 //’module_map_cache_key’ => $stringKey,
41
42 // The path in which to cache merged configuration.
43 //’cache_dir’ => $stringPath,
44
45 // Whether or not to enable modules dependency checking.
46 // Enabled by default, prevents usage of modules that depend on other modules
47 // that weren’t loaded.
48 // ’check_dependencies’ => true,
49 ),
50
51 // Used to create an own service manager. May contain one or more child arrays.
52 //’service_listener_options’ => array(
53 // array(
54 // ’service_manager’ => $stringServiceManagerName,
55 // ’config_key’ => $stringConfigKey,
56 // ’interface’ => $stringOptionalInterface,
57 // ’method’ => $stringRequiredMethodName,
58 // ),
59 // )
60
61 // Initial configuration with which to seed the ServiceManager.
62 // Should be compatible with ZendServiceManagerConfig.
63 // ’service_manager’ => array(),
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, with
the exception of the values under the ‘service_manager’ key, it cannot be overridden by a module.
This leads us to our first trick: how do you provide environment-specific system configuration?
116 Chapter 29. Advanced Configuration Tricks
Zend Framework 2 Documentation, Release 2.3.1dev
29.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 <?php
2 $env = getenv(’APP_ENV’) ?: ’production’;
3
4 // Use the $env value to determine which modules to load
5 $modules = array(
6 ’Application’,
7 );
8 if ($env == ’development’) {
9 $modules[] = ’ZendDeveloperTools’;
10 }
11
12 return array(
13 ’modules’ => $modules,
14
15 ’module_listener_options’ => array(
16 ’module_paths’ => array(
17 ’./module’,
18 ’./vendor’,
19 ),
20
21 ’config_glob_paths’ => array(
22 ’config/autoload/{,*.}{global,local}.php’,
23 ),
24
25 // Use the $env value to determine the state of the flag
26 ’config_cache_enabled’ => ($env == ’production’),
27
28 ’config_cache_key’ => ’app_config’,
29
30 // Use the $env value to determine the state of the flag
31 ’module_map_cache_enabled’ => ($env == ’production’),
32
33 ’module_map_cache_key’ => ’module_map’,
34
35 ’cache_dir’ => ’data/config/’,
29.1. System configuration 117
Zend Framework 2 Documentation, Release 2.3.1dev
36
37 // Use the $env value to determine the state of the flag
38 ’check_dependencies’ => ($env != ’production’),
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?
29.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 ser-
vice 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 configura-
tion – 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 devel-
opment 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:
118 Chapter 29. Advanced Configuration Tricks
Zend Framework 2 Documentation, Release 2.3.1dev
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).
29.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.
29.2. Module Configuration 119
Zend Framework 2 Documentation, Release 2.3.1dev
29.3 Configuration mapping table
Manager name Interface name Module Method name Config key
name
ControllerPluginManagerControllerPluginProviderInterfacegetControllerPluginConfig()controller_plugins
ControllerManager ControllerProviderInterfacegetControllerConfig()controllers
FilterManager FilterProviderInterface getFilterConfig() filters
FormElementManager FormElementProviderInterfacegetFormElementConfig()form_elements
HydratorManager HydratorProviderInterfacegetHydratorConfig() hydrators
InputFilterManager InputFilterProviderInterfacegetInputFilterConfig()input_filters
RoutePluginManager RouteProviderInterface getRouteConfig() route_manager
SerializerAdapterManagerSerializerProviderInterfacegetSerializerConfig()serializers
ServiceLocator ServiceProviderInterface getServiceConfig() service_manager
ValidatorManager ValidatorProviderInterfacegetValidatorConfig() validators
ViewHelperManager ViewHelperProviderInterfacegetViewHelperConfig()view_helpers
LogProcessorManagerLogProcessorProviderInterfacegetLogProcessorConfiglog_processors
LogWriterManager LogWriterProviderInterfacegetLogWriterConfig log_writers
29.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 the various service configuration methods in a module class
• configuration returned by getConfig()
In other words, your getConfig() win over the various service configuration methods. Additionally, and of partic-
ular note: the configuration returned from those methods will not be cached.
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 con-
figuration files in other markup formats.
29.5 Manipulating merged configuration
Occasionally you will want to not just override an application configuration key, but actually remove it. Since merging
will not remove keys, how can you handle this?
ZendModuleManagerListenerConfigListener triggers a special event,
ZendModuleManagerModuleEvent::EVENT_MERGE_CONFIG, after merging all configuration, but
prior to it being passed to the ServiceManager. By listening to this event, you can inspect the merged
configuration and manipulate it.
The ConfigListener itself listens to the event at priority 1000 (i.e., very high), which is when the configuration
is merged. You can tie into this to modify the merged configuration from your module, via the init() method.
1 namespace Foo;
2
3 use ZendModuleManagerModuleEvent;
4 use ZendModuleManagerModuleManager;
5
120 Chapter 29. Advanced Configuration Tricks
Zend Framework 2 Documentation, Release 2.3.1dev
6 class Module
7 {
8 public function init(ModuleManager $moduleManager)
9 {
10 $events = $moduleManager->getEventManager();
11
12 // Registering a listener at default priority, 1, which will trigger
13 // after the ConfigListener merges config.
14 $events->attach(ModuleEvent::EVENT_MERGE_CONFIG, array($this, ’onMergeConfig’));
15 }
16
17 public function onMergeConfig(ModuleEvent $e)
18 {
19 $configListener = $e->getConfigListener();
20 $config = $configListener->getMergedConfig(false);
21
22 // Modify the configuration; here, we’ll remove a specific key:
23 if (isset($config[’some_key’])) {
24 unset($config[’some_key’]);
25 }
26
27 // Pass the changed configuration back to the listener:
28 $configListener->setMergedConfig($config);
29 }
30 }
At this point, the merged application configuration will no longer contain the key some_key.
Note: If a cached config is used by the ModuleManager, the EVENT_MERGE_CONFIG event will not be triggered.
However, typically that means that what is cached will be what was originally manipulated by your listener.
29.6 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 ini-
tialize the system.
• Application configuration
– The ModuleManager loops through each module class in the order defined in the system configuration
* 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.
29.6. Configuration merging workflow 121
Zend Framework 2 Documentation, Release 2.3.1dev
– ConfigListener triggers EVENT_MERGE_CONFIG: - ConfigListener merges configuration -
Any other event listeners manipulate the configuration
– Merged configuration is finally passed to the ServiceManager
122 Chapter 29. Advanced Configuration Tricks
CHAPTER 30
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.
30.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 ’home’ => array(
2 ’type’ => ’ZendMvcRouterHttpLiteral’,
3 ’options’ => array(
4 ’route’ => ’/’,
5 ’defaults’ => array(
6 ’controller’ => ’ApplicationControllerIndex’, // <-- change back here
7 ’action’ => ’index’,
8 ),
9 ),
10 ),
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.
30.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
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
123
Zend Framework 2 Documentation, Release 2.3.1dev
1 ’service_manager’ => array(
2 ’factories’ => array(
3 ’navigation’ => ’ZendNavigationServiceDefaultNavigationFactory’, // <-- add this
4 ),
5 ),
30.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 return array(
2 ...
3 ’navigation’ => array(
4 ’default’ => array(
5 array(
6 ’label’ => ’Home’,
7 ’route’ => ’home’,
8 ),
9 array(
10 ’label’ => ’Album’,
11 ’route’ => ’album’,
12 ’pages’ => array(
13 array(
14 ’label’ => ’Add’,
15 ’route’ => ’album’,
16 ’action’ => ’add’,
17 ),
18 array(
19 ’label’ => ’Edit’,
20 ’route’ => ’album’,
21 ’action’ => ’edit’,
22 ),
23 array(
24 ’label’ => ’Delete’,
25 ’route’ => ’album’,
26 ’action’ => ’delete’,
27 ),
28 ),
29 ),
30 ),
31 ),
32 ...
33 );
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.
124 Chapter 30. Using ZendNavigation in your Album Module
Zend Framework 2 Documentation, Release 2.3.1dev
30.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 <div class="collapse navbar-collapse">
3 <ul class="nav navbar-nav">
4 <?php // <-- Add this !!
5 echo $this->navigation(’navigation’)->menu();
6 ?>
7 ...
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 <div class="collapse navbar-collapse">
2 <ul class="nav navbar-nav">
3 <?php // <-- Update this !!
4 echo $this->navigation(’navigation’)
5 ->menu()
6 ->setMinDepth(0)
7 ->setMaxDepth(0)
8 ->setUlClass(’nav navbar-nav’);
9 ?>
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.
30.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 <div class="container">
3 <?php echo $this->navigation(’navigation’)->breadcrumbs()->setMinDepth(0); // <-- Add this!! ?>
4 <?php echo $this->content; ?>
5 </div>
6 ...
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
30.4. Adding the Menu View Helper 125
Zend Framework 2 Documentation, Release 2.3.1dev
1 <ul class="breadcrumb">
2 <?php
3 // iterate through the pages
4 foreach ($this->pages as $key => $page):
5 ?>
6 <li>
7 <?php
8 // if this isn’t the last page, add a link and the separator
9 if ($key < count($this->pages) - 1):
10 ?>
11 <a href="<?php echo $page->getHref(); ?>"><?php echo $page->getLabel(); ?></a>
12 <?php
13 // otherwise, just output the name
14 else:
15 ?>
16 <?php echo $page->getLabel(); ?>
17 <?php endif; ?>
18 </li>
19 <?php endforeach; ?>
20 </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 <div class="container">
3 <?php
4 echo $this->navigation(’navigation’) // <-- Update this!!
5 ->breadcrumbs()
6 ->setMinDepth(0)
7 ->setPartial(array(’partial/breadcrumb.phtml’, ’Album’));
8 ?>
9 <?php echo $this->content; ?>
10 </div>
11 ...
Refreshing the page now gives us a lovely styled set of breadcrumbs on each page.
126 Chapter 30. Using ZendNavigation in your Album Module
CHAPTER 31
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:
31.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’),
(’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)’),
127
Zend Framework 2 Documentation, Release 2.3.1dev
(’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 Misérables (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’),
(’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’),
128 Chapter 31. Using ZendPaginator in your Album Module
Zend Framework 2 Documentation, Release 2.3.1dev
(’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 Fältskog’, ’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)’),
(’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’),
31.1. Preparation 129
Zend Framework 2 Documentation, Release 2.3.1dev
(’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.
31.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 <?php
2 namespace AlbumModel;
3
4 use ZendDbResultSetResultSet;
5 use ZendDbTableGatewayTableGateway;
6 use ZendDbSqlSelect;
7 use ZendPaginatorAdapterDbSelect;
8 use ZendPaginatorPaginator;
9
10 class AlbumTable
11 {
12 ...
13 public function fetchAll($paginated=false)
14 {
15 if ($paginated) {
16 // create a new Select object for the table album
17 $select = new Select(’album’);
18 // create a new result set based on the Album entity
19 $resultSetPrototype = new ResultSet();
20 $resultSetPrototype->setArrayObjectPrototype(new Album());
21 // create a new pagination adapter object
22 $paginatorAdapter = new DbSelect(
23 // our configured select object
24 $select,
25 // the adapter to run it against
26 $this->tableGateway->getAdapter(),
27 // the result set to hydrate
28 $resultSetPrototype
29 );
30 $paginator = new Paginator($paginatorAdapter);
31 return $paginator;
130 Chapter 31. Using ZendPaginator in your Album Module
Zend Framework 2 Documentation, Release 2.3.1dev
32 }
33 $resultSet = $this->tableGateway->select();
34 return $resultSet;
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.
31.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 public function indexAction()
3 {
4 // grab the paginator from the AlbumTable
5 $paginator = $this->getAlbumTable()->fetchAll(true);
6 // set the current page to what has been passed in query string, or to 1 if none set
7 $paginator->setCurrentPageNumber((int) $this->params()->fromQuery(’page’, 1));
8 // set the number of items per page to 10
9 $paginator->setItemCountPerPage(10);
10
11 return new ViewModel(array(
12 ’paginator’ => $paginator
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.
31.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:
module/Album/view/album/album/index.phtml
1 <table class="table">
2 <tr>
3 <th>Title</th>
4 <th>Artist</th>
5 <th>&nbsp;</th>
6 </tr>
7 <?php foreach ($this->paginator as $album) : // <-- change here! ?>
8 <tr>
9 <td><?php echo $this->escapeHtml($album->title);?></td>
10 <td><?php echo $this->escapeHtml($album->artist);?></td>
31.3. Modifying the AlbumController 131
Zend Framework 2 Documentation, Release 2.3.1dev
11 <td>
12 <a href="<?php echo $this->url(’album’,
13 array(’action’ => ’edit’, ’id’ => $album->id));?>">Edit</a>
14 <a href="<?php echo $this->url(’album’,
15 array(’action’ => ’delete’, ’id’ => $album->id));?>">Delete</a>
16 </td>
17 </tr>
18 <?php endforeach; ?>
19 </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...
31.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 <?php if ($this->pageCount): ?>
2 <div>
3 <ul class="pagination">
4 <!-- Previous page link -->
5 <?php if (isset($this->previous)): ?>
6 <li>
7 <a href="<?php echo $this->url($this->route); ?>?page=<?php echo $this->previous
8 <<
9 </a>
10 </li>
11 <?php else: ?>
12 <li class="disabled">
13 <a href="#">
14 <<
15 </a>
16 </li>
17 <?php endif; ?>
18
19 <!-- Numbered page links -->
20 <?php foreach ($this->pagesInRange as $page): ?>
21 <?php if ($page != $this->current): ?>
22 <li>
23 <a href="<?php echo $this->url($this->route);?>?page=<?php echo $page; ?>">
24 <?php echo $page; ?>
25 </a>
26 </li>
27 <?php else: ?>
28 <li class="active">
29 <a href="#"><?php echo $page; ?></a>
30 </li>
31 <?php endif; ?>
32 <?php endforeach; ?>
33
132 Chapter 31. Using ZendPaginator in your Album Module
Zend Framework 2 Documentation, Release 2.3.1dev
34 <!-- Next page link -->
35 <?php if (isset($this->next)): ?>
36 <li>
37 <a href="<?php echo $this->url($this->route); ?>?page=<?php echo $this->next; ?>
38 >>
39 </a>
40 </li>
41 <?php else: ?>
42 <li class="disabled">
43 <a href="#">
44 >>
45 </a>
46 </li>
47 <?php endif; ?>
48 </ul>
49 </div>
50 <?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.
31.5. Creating the Pagination Control Partial 133
Zend Framework 2 Documentation, Release 2.3.1dev
134 Chapter 31. Using ZendPaginator in your Album Module
CHAPTER 32
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 <?php
3 // add at the end of the file after the table
4 echo $this->paginationControl(
5 // the paginator object
6 $this->paginator,
7 // the scrolling style
8 ’sliding’,
9 // the partial to use to render the control
10 array(’partial/paginator.phtml’, ’Album’),
11 // the route to link to when a user clicks a control link
12 array(
13 ’route’ => ’album’
14 )
15 );
16 ?>
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!
135
Zend Framework 2 Documentation, Release 2.3.1dev
136 Chapter 32. Using the PaginationControl View Helper
CHAPTER 33
Setting up a database adapter
33.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.
33.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 return array(
4 ’db’ => array(
5 ’driver’ => ’Pdo’,
6 ’dsn’ => ’mysql:dbname=zf2tutorial;host=localhost’,
7 ),
8 ’service_manager’ => array(
9 ’factories’ => array(
10 ’ZendDbAdapterAdapter’ => ’ZendDbAdapterAdapterServiceFactory’,
11 ),
12 ),
13 );
The adapter can then be accessed in any ServiceLocatorAware classes.
1 public function getAdapter()
2 {
3 if (!$this->adapter) {
4 $sm = $this->getServiceLocator();
5 $this->adapter = $sm->get(’ZendDbAdapterAdapter’);
6 }
7 return $this->adapter;
8 }
More information on adapter options can be found in the docs for ZendDbAdapter.
137
Zend Framework 2 Documentation, Release 2.3.1dev
33.3 Setting a static adapter
In order to utilize this adapter in non-ServiceLocatorAware classes, you can use
ZendDbTableGatewayFeatureGlobalAdapterFeature::setStaticAdapter() to set a
static adapter:
1 // config/autoload/global.php
2
3 return array(
4 ’db’ => array(
5 ’driver’ => ’Pdo’,
6 ’dsn’ => ’mysql:dbname=zf2tutorial;host=localhost’,
7 ),
8 ’service_manager’ => array(
9 ’factories’ => array(
10 ’ZendDbAdapterAdapter’ => function ($serviceManager) {
11 $adapterFactory = new ZendDbAdapterAdapterServiceFactory();
12 $adapter = $adapterFactory->createService($serviceManager);
13
14 ZendDbTableGatewayFeatureGlobalAdapterFeature::setStaticAdapter($adapter);
15
16 return $adapter;
17 }
18 ),
19 ),
20 );
The adapter can then later be fetched using ZendDbTableGatewayFeatureGlobalAdapterFeature::getStaticAda
for use in e.g. ZendValidatorDbRecordExists:
1 $validator = new ZendValidatorDbRecordExists(
2 array(
3 ’table’ => ’users’,
4 ’field’ => ’emailaddress’,
5 ’adapter’ => ZendDbTableGatewayFeatureGlobalAdapterFeature::getStaticAdapter()
6 )
7 );
138 Chapter 33. Setting up a database adapter
CHAPTER 34
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.
139
Zend Framework 2 Documentation, Release 2.3.1dev
140 Chapter 34. Migration from Zend Framework 1
CHAPTER 35
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.
35.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.
141
Zend Framework 2 Documentation, Release 2.3.1dev
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.
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
142 Chapter 35. Namespacing Old Classes
Zend Framework 2 Documentation, Release 2.3.1dev
inheritance), model and form classes might not have the same level of coupling.
35.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 <?php return array (
2 array (
3 ’root_directory’ => ’/realpath/to/project/application/models’,
4 ’original_class’ => ’Application_Model_Invoice’,
5 ’original_file’ => ’/realpath/to/project/application/models/Invoice.php’,
6 ’new_namespace’ => ’ApplicationModel’,
7 ’new_class’ => ’Invoice’,
8 ’new_file’ => ’/realpath/to/project/application/models/Application/Model/Invoice.php’,
9 ),
10 ...
11 );
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: models/DbTable/Invoice.php -> models/Application/Model/DbTable/Invoice.php
new file: models/Application/Model/DbTable/Transaction.php
renamed: models/Invoice.php -> models/Application/Model/Invoice.php
renamed: models/Payment/Paypal/DirectPayment.php -> models/Application/Model/Payment/Paypal/DirectPa
renamed: models/PurchaseOrder.php -> models/Application/Model/PurchaseOrder.php
renamed: models/PurchaseOrderRepository.php -> models/Application/Model/PurchaseOrderRepository.php
new file: models/Application/Model/PurchaseOrderService.php
renamed: models/Purchaser.php -> models/Application/Model/Purchaser.php
renamed: models/Ticket.php -> models/Application/Model/Ticket.php
renamed: models/Transaction.php -> models/Application/Model/Transaction.php
renamed: models/TransactionRepository.php -> models/Application/Model/TransactionRepository.php
deleted: models/DbTable/Transaction.php
deleted: models/PurchaseOrderService.php
35.2. HOWTO Namespace Your Models 143
Zend Framework 2 Documentation, Release 2.3.1dev
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 use ApplicationModelPurchaseOrder;
4 use ApplicationModelTransaction;
5 use Zend_Filter_Alnum;
6
7 class Invoice
8 {
9
10 protected $tickets;
11 protected $transaction;
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.
144 Chapter 35. Namespacing Old Classes
CHAPTER 36
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.
36.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 define(’ZF2_PATH’, ’/path/to/zf2/library’);
2 require_once ZF2_PATH . ’/Zend/Loader/StandardAutoloader.php’;
3 $loader = new ZendLoaderStandardAutoloader(array(
4 ’autoregister_zf’ => true,
5 ));
6 $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’)).
36.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.
145
Zend Framework 2 Documentation, Release 2.3.1dev
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.23",
"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.
36.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 SetEnv APPLICATION_ENV development
2 RewriteEngine On
3 RewriteCond %{REQUEST_FILENAME} -s [OR]
4 RewriteCond %{REQUEST_FILENAME} -l [OR]
5 RewriteCond %{REQUEST_FILENAME} -d
6 RewriteRule ^ - [NC,L]
7 RewriteRule ^album(/.*)?$ index_zf2.php [NC,L]
8 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.
146 Chapter 36. Running Zend Framework 2 and Zend Framework 1 in parallel
CHAPTER 37
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 authenti-
cation adapters and persistent storage backends.
37.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 creden-
tials (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 class MyAuthAdapter implements AdapterInterface
4 {
5 /**
6 * Sets username and password for authentication
7 *
8 * @return void
9 */
147
Zend Framework 2 Documentation, Release 2.3.1dev
10 public function __construct($username, $password)
11 {
12 // ...
13 }
14
15 /**
16 * Performs an authentication attempt
17 *
18 * @return ZendAuthenticationResult
19 * @throws ZendAuthenticationAdapterExceptionExceptionInterface
20 * If authentication cannot be performed
21 */
22 public function authenticate()
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.
37.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 au-
thentication 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 op-
erations. 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 Result::SUCCESS
4 Result::FAILURE
5 Result::FAILURE_IDENTITY_NOT_FOUND
6 Result::FAILURE_IDENTITY_AMBIGUOUS
7 Result::FAILURE_CREDENTIAL_INVALID
8 Result::FAILURE_UNCATEGORIZED
148 Chapter 37. Introduction to ZendAuthentication
Zend Framework 2 Documentation, Release 2.3.1dev
The following example illustrates how a developer may branch on the result code:
1 // inside of AuthController / loginAction
2 $result = $this->auth->authenticate($adapter);
3
4 switch ($result->getCode()) {
5
6 case Result::FAILURE_IDENTITY_NOT_FOUND:
7 /** do stuff for nonexistent identity **/
8 break;
9
10 case Result::FAILURE_CREDENTIAL_INVALID:
11 /** do stuff for invalid credential **/
12 break;
13
14 case Result::SUCCESS:
15 /** do stuff for successful authentication **/
16 break;
17
18 default:
19 /** do stuff for other failure **/
20 break;
21 }
37.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.
37.3.1 Default Persistence in the PHP Session
By default, ZendAuthentication provides persistent storage of the identity from a suc-
cessful authentication attempt using the PHP session. Upon a successful authentication at-
tempt, ZendAuthenticationAuthenticationService::authenticate() stores
the identity from the authentication result into persistent storage. Unless specified other-
wise, 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.
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
37.3. Identity Persistence 149
Zend Framework 2 Documentation, Release 2.3.1dev
constructor of ZendSessionContainer. This should occur before authentication is attempted, since
ZendAuthenticationAuthenticationService::authenticate() performs the automatic
storage of the identity.
1 use ZendAuthenticationAuthenticationService;
2 use ZendAuthenticationStorageSession as SessionStorage;
3
4 $auth = new AuthenticationService();
5
6 // Use ’someNamespace’ instead of ’Zend_Auth’
7 $auth->setStorage(new SessionStorage(’someNamespace’));
8
9 /**
10 * @todo Set up the auth adapter, $authAdapter
11 */
12
13 // Authenticate, saving the result, and persisting the identity on
14 // success
15 $result = $auth->authenticate($authAdapter);
37.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 $storage = new Chain;
2 $storage->add(new Session);
3 $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 $chain->add(new A, 2);
2 $chain->add(new B, 10); // First use B
37.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-
ment ZendAuthenticationStorageStorageInterface and supply an instance of the class to
ZendAuthenticationAuthenticationService::setStorage().
150 Chapter 37. Introduction to ZendAuthentication
Zend Framework 2 Documentation, Release 2.3.1dev
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 class MyStorage implements StorageInterface
4 {
5 /**
6 * Returns true if and only if storage is empty
7 *
8 * @throws ZendAuthenticationExceptionExceptionInterface
9 * If it is impossible to
10 * determine whether storage is empty
11 * @return boolean
12 */
13 public function isEmpty()
14 {
15 /**
16 * @todo implementation
17 */
18 }
19
20 /**
21 * Returns the contents of storage
22 *
23 * Behavior is undefined when storage is empty.
24 *
25 * @throws ZendAuthenticationExceptionExceptionInterface
26 * If reading contents from storage is impossible
27 * @return mixed
28 */
29
30 public function read()
31 {
32 /**
33 * @todo implementation
34 */
35 }
36
37 /**
38 * Writes $contents to storage
39 *
40 * @param mixed $contents
41 * @throws ZendAuthenticationExceptionExceptionInterface
42 * If writing $contents to storage is impossible
43 * @return void
44 */
45
46 public function write($contents)
47 {
48 /**
49 * @todo implementation
50 */
51 }
52
53 /**
37.3. Identity Persistence 151
Zend Framework 2 Documentation, Release 2.3.1dev
54 * Clears contents from storage
55 *
56 * @throws ZendAuthenticationExceptionExceptionInterface
57 * If clearing contents from storage is impossible
58 * @return void
59 */
60
61 public function clear()
62 {
63 /**
64 * @todo implementation
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 // Instruct AuthenticationService to use the custom storage class
4 $auth = new AuthenticationService();
5
6 $auth->setStorage(new MyStorage());
7
8 /**
9 * @todo Set up the auth adapter, $authAdapter
10 */
11
12 // Authenticate, saving the result, and persisting the identity on
13 // success
14 $result = $auth->authenticate($authAdapter);
37.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 // instantiate the authentication service
4 $auth = new AuthenticationService();
5
6 // Set up the authentication adapter
7 $authAdapter = new MyAuthAdapter($username, $password);
8
9 // Attempt authentication, saving the result
10 $result = $auth->authenticate($authAdapter);
11
12 if (!$result->isValid()) {
13 // Authentication failed; print the reasons why
152 Chapter 37. Introduction to ZendAuthentication
Zend Framework 2 Documentation, Release 2.3.1dev
14 foreach ($result->getMessages() as $message) {
15 echo "$messagen";
16 }
17 } else {
18 // Authentication succeeded; the identity ($username) is stored
19 // in the session
20 // $result->getIdentity() === $auth->getIdentity()
21 // $result->getIdentity() === $username
22 }
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 * @todo Set up the auth adapter, $authAdapter
7 */
8
9 if ($auth->hasIdentity()) {
10 // Identity exists; get it
11 $identity = $auth->getIdentity();
12 }
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 di-
rectly. 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 // Set up the authentication adapter
2 $authAdapter = new MyAuthAdapter($username, $password);
3
4 // Attempt authentication, saving the result
5 $result = $authAdapter->authenticate();
6
7 if (!$result->isValid()) {
8 // Authentication failed; print the reasons why
9 foreach ($result->getMessages() as $message) {
10 echo "$messagen";
11 }
12 } else {
13 // Authentication succeeded
14 // $result->getIdentity() === $username
15 }
37.4. Usage 153
Zend Framework 2 Documentation, Release 2.3.1dev
154 Chapter 37. Introduction to ZendAuthentication
CHAPTER 38
Database Table Authentication
38.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, ob-
scured, 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.
38.2 Basic Usage
As explained in the introduction, the ZendAuthenticationAdapterDbTable constructor requires an in-
stance 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:
1 use ZendDbAdapterAdapter as DbAdapter;
2
3 // Create a SQLite database connection
4 $dbAdapter = new DbAdapter(array(
155
Zend Framework 2 Documentation, Release 2.3.1dev
5 ’driver’ => ’Pdo_Sqlite’,
6 ’database’ => ’path/to/sqlite.db’
7 ));
8
9 // Build a simple table creation query
10 $sqlCreate = ’CREATE TABLE [users] (’
11 . ’[id] INTEGER NOT NULL PRIMARY KEY, ’
12 . ’[username] VARCHAR(50) UNIQUE NOT NULL, ’
13 . ’[password] VARCHAR(32) NULL, ’
14 . ’[real_name] VARCHAR(150) NULL)’;
15
16 // Create the authentication credentials table
17 $dbAdapter->query($sqlCreate);
18
19 // Build a query to insert a row for which authentication may succeed
20 $sqlInsert = "INSERT INTO users (username, password, real_name) "
21 . "VALUES (’my_username’, ’my_password’, ’My Real Name’)";
22
23 // Insert the data
24 $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 // Configure the instance with constructor parameters...
4 $authAdapter = new AuthAdapter($dbAdapter,
5 ’users’,
6 ’username’,
7 ’password’
8 );
9
10 // ...or configure the instance with setter methods
11 $authAdapter = new AuthAdapter($dbAdapter);
12
13 $authAdapter
14 ->setTableName(’users’)
15 ->setIdentityColumn(’username’)
16 ->setCredentialColumn(’password’)
17 ;
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 // Set the input credential values (e.g., from a login form)
2 $authAdapter
3 ->setIdentity(’my_username’)
4 ->setCredential(’my_password’)
5 ;
6
7 // Perform the authentication query, saving the result
In addition to the availability of the getIdentity() method upon the authentication result object,
ZendAuthenticationAdapterDbTable also supports retrieving the table row upon authentication suc-
cess:
156 Chapter 38. Database Table Authentication
Zend Framework 2 Documentation, Release 2.3.1dev
1 // Print the identity
2 echo $result->getIdentity() . "nn";
3
4 // Print the result row
5 print_r($authAdapter->getResultRowObject());
6
7 /* Output:
8 my_username
9
10 Array
11 (
12 [id] => 1
13 [username] => my_username
14 [password] => my_password
15 [real_name] => My Real Name
16 )
17 */
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 $columnsToReturn = array(
2 ’id’, ’username’, ’real_name’
3 );
4 print_r($authAdapter->getResultRowObject($columnsToReturn));
5
6 /* Output:
7
8 Array
9 (
10 [id] => 1
11 [username] => my_username
12 [real_name] => My Real Name
13 )
14 */
15
16 $columnsToOmit = array(’password’);
17 print_r($authAdapter->getResultRowObject(null, $columnsToOmit);
18
19 /* Output:
20
21 Array
22 (
23 [id] => 1
24 [username] => my_username
25 [real_name] => My Real Name
26 )
27 */
38.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:
38.3. Advanced Usage: Persisting a DbTable Result Object 157
Zend Framework 2 Documentation, Release 2.3.1dev
1 // authenticate with ZendAuthenticationAdapterDbTable
2 $result = $this->_auth->authenticate($adapter);
3
4 if ($result->isValid()) {
5 // store the identity as an object where only the username and
6 // real_name have been returned
7 $storage = $this->_auth->getStorage();
8 $storage->write($adapter->getResultRowObject(array(
9 ’username’,
10 ’real_name’,
11 )));
12
13 // store the identity as an object where the password column has
14 // been omitted
15 $storage->write($adapter->getResultRowObject(
16 null,
17 ’password’
18 ));
19
20 /* ... */
21
22 } else {
23
24 /* ... */
25
26 }
38.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 mecha-
nisms that can be leveraged for additional checks at authentication time to solve some common user problems.
1 use ZendAuthenticationAdapterDbTable as AuthAdapter;
2
3 // The status field value of an account is not equal to "compromised"
4 $adapter = new AuthAdapter($db,
5 ’users’,
6 ’username’,
7 ’password’,
8 ’MD5(?) AND status != "compromised"’
9 );
10
11 // The active field value of an account is equal to "TRUE"
12 $adapter = new AuthAdapter($db,
13 ’users’,
14 ’username’,
15 ’password’,
16 ’MD5(?) AND active = "TRUE"’
17 );
Another scenario can be the implementation of a salting mechanism. Salting is a term referring to a technique which
158 Chapter 38. Database Table Authentication
Zend Framework 2 Documentation, Release 2.3.1dev
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 $sqlAlter = "ALTER TABLE [users] "
2 . "ADD COLUMN [password_salt] "
3 . "AFTER [password]";
Here’s a simple way to generate a salt string for every user at registration:
1 $dynamicSalt = ’’;
2 for ($i = 0; $i < 50; $i++) {
3 $dynamicSalt .= chr(rand(33, 126));
4 }
And now let’s build the adapter:
1 $adapter = new AuthAdapter($db,
2 ’users’,
3 ’username’,
4 ’password’,
5 "MD5(CONCAT(’staticSalt’, ?, password_salt))"
6 );
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 // Continuing with the example from above
2 $adapter = new AuthAdapter($db,
3 ’users’,
4 ’username’,
5 ’password’,
6 ’MD5(?)’
7 );
8
9 // get select object (by reference)
10 $select = $adapter->getDbSelect();
11 $select->where(’active = "TRUE"’);
12
13 // authenticate, this ensures that users.active = TRUE
14 $adapter->authenticate();
38.3. Advanced Usage: Persisting a DbTable Result Object 159
Zend Framework 2 Documentation, Release 2.3.1dev
160 Chapter 38. Database Table Authentication
CHAPTER 39
Digest Authentication
39.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
39.2 Specifics
The digest authentication adapter, ZendAuthenticationAdapterDigest, requires several input parame-
ters:
• 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().
39.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.
161
Zend Framework 2 Documentation, Release 2.3.1dev
1 use ZendAuthenticationAdapterDigest as AuthAdapter;
2
3 $adapter = new AuthAdapter($filename,
4 $realm,
5 $username,
6 $password);
7
8 $result = $adapter->authenticate();
9
10 $identity = $result->getIdentity();
11
12 print_r($identity);
13
14 /*
15 Array
16 (
17 [realm] => Some Realm
18 [username] => someUser
19 )
20 */
162 Chapter 39. Digest Authentication
CHAPTER 40
HTTP Authentication Adapter
40.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.
40.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.
40.3 Configuration Options
The ZendAuthenticationAdapterHttp class requires a configuration array passed to its constructor.
There are several configuration options available, and some are required:
163
Zend Framework 2 Documentation, Release 2.3.1dev
Table 40.1: Configuration Options
Option
Name
Required Description
ac-
cept_schemes
Yes Determines which authentication schemes the adapter will accept from the
client. Must be a space-separated list containing ‘basic’ and/or ‘digest’.
realm Yes Sets the authentication realm; usernames should be unique within a given
realm.
di-
gest_domains
Yes, when
accept_schemes
contains digest
Space-separated list of URIs for which the same authentication information
is valid. The URIs need not all point to the same server.
nonce_timeoutYes, when
accept_schemes
contains digest
Sets the number of seconds for which the nonce is valid. See notes below.
use_opaqueNo Specifies whether to send the opaque value in the header. True by default.
algo-
rithm
No Specified the algorithm. Defaults to MD5, the only supported option (for
now).
proxy_authNo 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 sup-
posed 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 cre-
dentials every hour, on the hour. This will be resolved in a future release, once nonce tracking and stale support are
implemented.
40.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.
40.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
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:
164 Chapter 40. HTTP Authentication Adapter
Zend Framework 2 Documentation, Release 2.3.1dev
1 use ZendAuthenticationAdapterHttpFileResolver;
2 $path = ’files/passwd.txt’;
3 $resolver = new FileResolver($path);
or
1 $path = ’files/passwd.txt’;
2 $resolver = new FileResolver();
3 $resolver->setFile($path);
If the given path is empty or not readable, an exception is thrown.
40.5 Basic Usage
First, set up an array with the required configuration values:
1 $config = array(
2 ’accept_schemes’ => ’basic digest’,
3 ’realm’ => ’My Web Site’,
4 ’digest_domains’ => ’/members_only /my_account’,
5 ’nonce_timeout’ => 3600,
6 );
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 $basicResolver = new FileResolver();
4 $basicResolver->setFile(’files/basicPasswd.txt’);
5
6 $digestResolver = new FileResolver();
7 $digestResolver->setFile(’files/digestPasswd.txt’);
8
9 $adapter->setBasicResolver($basicResolver);
10 $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 assert($request instanceof ZendHttpRequest);
2 assert($response instanceof ZendHttpResponse);
3
4 $adapter->setRequest($request);
5 $adapter->setResponse($response);
6
7 $result = $adapter->authenticate();
8 if (!$result->isValid()) {
9 // Bad username/password, or canceled password prompt
10 }
40.5. Basic Usage 165
Zend Framework 2 Documentation, Release 2.3.1dev
166 Chapter 40. HTTP Authentication Adapter
CHAPTER 41
LDAP Authentication
41.1 Introduction
ZendAuthenticationAdapterLdap supports web application authentication with LDAP services. Its fea-
tures 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.
41.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 use ZendAuthenticationAuthenticationService;
2 use ZendAuthenticationAdapterLdap as AuthAdapter;
3 use ZendConfigReaderIni as ConfigReader;
4 use ZendConfigConfig;
5 use ZendLogLogger;
6 use ZendLogWriterStream as LogWriter;
7 use ZendLogFilterPriority as LogFilter;
8
9 $username = $this->getRequest()->getPost(’username’);
10 $password = $this->getRequest()->getPost(’password’);
11
12
13 $auth = new AuthenticationService();
14
15 $configReader = new ConfigReader();
16 $configData = $configReader->fromFile(’./ldap-config.ini’);
17 $config = new Config($configData, true);
18
19 $log_path = $config->production->ldap->log_path;
20 $options = $config->production->ldap->toArray();
21 unset($options[’log_path’]);
22
23 $adapter = new AuthAdapter($options,
167
Zend Framework 2 Documentation, Release 2.3.1dev
24 $username,
25 $password);
26
27 $result = $auth->authenticate($adapter);
28
29 if ($log_path) {
30 $messages = $result->getMessages();
31
32 $logger = new Logger;
33 $writer = new LogWriter($log_path);
34
35 $logger->addWriter($writer);
36
37 $filter = new LogFilter(Logger::DEBUG);
38 $writer->addFilter($filter);
39
40 foreach ($messages as $i => $message) {
41 if ($i-- > 1) { // $messages[2] and up are log messages
42 $message = str_replace("n", "n ", $message);
43 $logger->debug("Ldap: $i: $message");
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 ; Typical options for OpenLDAP
6 ldap.server1.host = s0.foo.net
7 ldap.server1.accountDomainName = foo.net
8 ldap.server1.accountDomainNameShort = FOO
9 ldap.server1.accountCanonicalForm = 3
10 ldap.server1.username = "CN=user1,DC=foo,DC=net"
11 ldap.server1.password = pass1
12 ldap.server1.baseDn = "OU=Sales,DC=foo,DC=net"
13 ldap.server1.bindRequiresDn = true
14
15 ; Typical options for Active Directory
16 ldap.server2.host = dc1.w.net
17 ldap.server2.useStartTls = true
18 ldap.server2.accountDomainName = w.net
19 ldap.server2.accountDomainNameShort = W
20 ldap.server2.accountCanonicalForm = 3
21 ldap.server2.baseDn = "CN=Users,DC=w,DC=net"
168 Chapter 41. LDAP Authentication
Zend Framework 2 Documentation, Release 2.3.1dev
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 multi-
ple 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 Win-
dows, we provide it here for name canonicalization purposes (described in the Username Canonicalization section
below).
41.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 Array
2 (
3 [server2] => Array
4 (
5 [host] => dc1.w.net
6 [useStartTls] => 1
7 [accountDomainName] => w.net
8 [accountDomainNameShort] => W
9 [accountCanonicalForm] => 3
10 [baseDn] => CN=Users,DC=w,DC=net
11 )
12
13 [server1] => Array
14 (
15 [host] => s0.foo.net
16 [accountDomainName] => foo.net
17 [accountDomainNameShort] => FOO
18 [accountCanonicalForm] => 3
19 [username] => CN=user1,DC=foo,DC=net
20 [password] => pass1
21 [baseDn] => OU=Sales,DC=foo,DC=net
22 [bindRequiresDn] => 1
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?
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 re-
verse. For example, the user account CN=Bob Carter,CN=Users,DC=w,DC=net is located directly within the
41.3. The API 169
Zend Framework 2 Documentation, Release 2.3.1dev
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 op-
tions 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). Alter-
natively, they may also be set with the setUsername() and setPassword() methods.
41.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():
170 Chapter 41. LDAP Authentication
Zend Framework 2 Documentation, Release 2.3.1dev
Table 41.1: Server Options
Name Description
host The hostname of LDAP server that these options represent. This option is required.
port 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-
icalForm
A value of 2, 3 or 4 indicating the form to which account names should be canonicalized after
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).
accountDo-
mainName
The FQDN domain name for which the target LDAP server is an authority (e.g., example.com).
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
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.
41.4. Server Options 171
Zend Framework 2 Documentation, Release 2.3.1dev
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.
41.5 Collecting Debugging Messages
ZendAuthenticationAdapterLdap collects debugging information within its authenticate()
method. This information is stored in the ZendAuthenticationResult object as messages. The array re-
turned by ZendAuthenticationResult::getMessages() is described as follows
Table 41.2: Debugging Messages
Messages
Array Index
Description
Index 0 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.
Index 1 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.
Indexes 2 and
higher
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).
41.6 Common Options for Specific Servers
41.6.1 Options for Active Directory
For ADS, the following options are noteworthy:
172 Chapter 41. LDAP Authentication
Zend Framework 2 Documentation, Release 2.3.1dev
Table 41.3: Options for Active Directory
Name Additional Notes
host As with all servers, this option is required.
useStartTls For the sake of security, this should be TRUE if the server has the necessary certificate installed.
useSsl Possibly used as an alternative to useStartTls (see above).
baseDn 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.
accountCanon-
icalForm
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.)
accountDo-
mainName
This is required with AD unless accountCanonicalForm 2 is used, which, again, is discouraged.
accountDo-
main-
NameShort
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.
41.6.2 Options for OpenLDAP
For OpenLDAP or a generic LDAP server using a typical posixAccount style schema, the following options are note-
worthy:
41.6. Common Options for Specific Servers 173
Zend Framework 2 Documentation, Release 2.3.1dev
Table 41.4: Options for OpenLDAP
Name Additional Notes
host As with all servers, this option is required.
useStartTls For the sake of security, this should be TRUE if the server has the necessary certificate installed.
useSsl Possibly used as an alternative to useStartTls (see above).
username 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-
icalForm
Optional, but the default value is 4 (principal style names like alice@foo.net), which may not
be ideal if your users are used to backslash style names (e.g., FOOalice). For backslash style
names use value 3.
accountDo-
mainName
Required unless you’re using accountCanonicalForm 2, which is not recommended.
accountDo-
main-
NameShort
If AD is not also being used, this value is not required. Otherwise, if accountCanonicalForm 3
is used, this option is required and should be a short name that corresponds adequately to the
accountDomainName (e.g., if your accountDomainName is foo.net, a good
accountDomainNameShort value might be FOO).
174 Chapter 41. LDAP Authentication
CHAPTER 42
Authentication Validator
42.1 Introduction
ZendAuthenticationValidatorAuthentication provides the ability to utilize a validator for an In-
putFilter 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
42.2 Basic Usage
1 use ZendAuthenticationAuthenticationService;
2 use ZendAuthenticationValidatorAuthentication as AuthenticationValidator;
3
4 $service = new AuthenticationService();
5 $adapter = new MyAuthenticationAdapter();
6 $validator = new AuthenticationValidator(
7 ’service’ => $service,
8 ’adapter’ => $adapter,
9 );
10
11 $validator->setCredential(’myCredentialContext’);
12 $validator->isValid(’myIdentity’, array(
13 ’myCredentialContext’ => ’myCredential’,
14 ));
175
Zend Framework 2 Documentation, Release 2.3.1dev
176 Chapter 42. Authentication Validator
CHAPTER 43
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.
177
Zend Framework 2 Documentation, Release 2.3.1dev
178 Chapter 43. Introduction to ZendBarcode
CHAPTER 44
Barcode creation using ZendBarcodeBarcode class
44.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 // Only the text to draw is required
4 $barcodeOptions = array(’text’ => ’ZEND-FRAMEWORK’);
5
6 // No required options
7 $rendererOptions = array();
8 $renderer = Barcode::factory(
9 ’code39’, ’image’, $barcodeOptions, $rendererOptions
10 );
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 use ZendConfigConfig;
2 use ZendBarcodeBarcode;
3
179
Zend Framework 2 Documentation, Release 2.3.1dev
4 // Using only one ZendConfigConfig object
5 $config = new Config(array(
6 ’barcode’ => ’code39’,
7 ’barcodeParams’ => array(’text’ => ’ZEND-FRAMEWORK’),
8 ’renderer’ => ’image’,
9 ’rendererParams’ => array(’imageType’ => ’gif’),
10 ));
11
12 $renderer = Barcode::factory($config);
44.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 // Only the text to draw is required
4 $barcodeOptions = array(’text’ => ’ZEND-FRAMEWORK’);
5
6 // No required options
7 $rendererOptions = array();
8
9 // Draw the barcode in a new image,
10 $imageResource = Barcode::factory(
11 ’code39’, ’image’, $barcodeOptions, $rendererOptions
12 )->draw();
Drawing a barcode with ZendBarcodeBarcode::draw()
1 use ZendBarcodeBarcode;
2
3 // Only the text to draw is required
4 $barcodeOptions = array(’text’ => ’ZEND-FRAMEWORK’);
5
6 // No required options
7 $rendererOptions = array();
8
9 // Draw the barcode in a new image,
10 $imageResource = Barcode::draw(
11 ’code39’, ’image’, $barcodeOptions, $rendererOptions
12 );
44.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.
180 Chapter 44. Barcode creation using ZendBarcodeBarcode class
Zend Framework 2 Documentation, Release 2.3.1dev
Rendering a barcode with the renderer object
1 use ZendBarcodeBarcode;
2
3 // Only the text to draw is required
4 $barcodeOptions = array(’text’ => ’ZEND-FRAMEWORK’);
5
6 // No required options
7 $rendererOptions = array();
8
9 // Draw the barcode in a new image,
10 // send the headers and the image
11 Barcode::factory(
12 ’code39’, ’image’, $barcodeOptions, $rendererOptions
13 )->render();
This will generate this barcode:
Rendering a barcode with ZendBarcodeBarcode::render()
1 use ZendBarcodeBarcode;
2
3 // Only the text to draw is required
4 $barcodeOptions = array(’text’ => ’ZEND-FRAMEWORK’);
5
6 // No required options
7 $rendererOptions = array();
8
9 // Draw the barcode in a new image,
10 // send the headers and the image
11 Barcode::render(
12 ’code39’, ’image’, $barcodeOptions, $rendererOptions
13 );
This will generate the same barcode as the previous example.
44.3. Rendering a barcode 181
Zend Framework 2 Documentation, Release 2.3.1dev
182 Chapter 44. Barcode creation using ZendBarcodeBarcode class
CHAPTER 45
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 // Case 1: constructor
6 $barcode = new ObjectCode39($options);
7
8 // Case 2: setOptions()
9 $barcode = new ObjectCode39();
10 $barcode->setOptions($options);
11
12 // Case 3: individual setters
13 $barcode = new ObjectCode39();
14 $barcode->setText(’ZEND-FRAMEWORK’)
15 ->setBarHeight(40);
45.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:
183
Zend Framework 2 Documentation, Release 2.3.1dev
Table 45.1: Common Options
Option Data
Type
Default
Value
Description
barcode-
Namespace
String ZendBarcodeObjectNamespace of the barcode; for example, if you need to extend the
embedding objects
barHeight Integer 50 Height of the bars
barThick-
Width
Integer 3 Width of the thick bar
barThin-
Width
Integer 1 Width of the thin bar
factor Integer 1 Factor by which to multiply bar widths and font sizes (barHeight,
barThinWidth, barThickWidth and fontSize)
foreColor Integer 0x000000
(black)
Color of the bar and the text. Could be provided as an integer or as a
HTML value (e.g. “#333333”)
background-
Color
Integer or
String
0xFFFFFF
(white)
Color of the background. Could be provided as an integer or as a
HTML value (e.g. “#333333”)
orientation Float 0 Orientation of the barcode
font String or
Integer
NULL Font path to a TTF font or a number between 1 and 5 if using image
generation with GD (internal fonts)
fontSize Float 10 Size of the font (not applicable with numeric fonts)
withBorder Boolean FALSE Draw a border around the barcode and the quiet zones
withQuiet-
Zones
Boolean TRUE Leave a quiet zone before and after the barcode
drawText Boolean TRUE Set if the text is displayed below the barcode
stretchText Boolean FALSE Specify if the text is stretched all along the barcode
withCheck-
sum
Boolean FALSE Indicate whether or not the checksum is automatically added to the
barcode
withCheck-
sumInText
Boolean FALSE Indicate whether or not the checksum is displayed in the textual
representation
text String NULL The text to represent as a barcode
45.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 individ-
ual objects by using the setFont() method.
1 use ZendBarcodeBarcode;
2
3 // In your bootstrap:
4 Barcode::setBarcodeFont(’my_font.ttf’);
5
6 // Later in your code:
7 Barcode::render(
8 ’code39’,
9 ’pdf’,
10 array(’text’ => ’ZEND-FRAMEWORK’)
11 ); // will use ’my_font.ttf’
12
13 // or:
14 Barcode::render(
15 ’code39’,
16 ’image’,
17 array(
184 Chapter 45. ZendBarcodeBarcode Objects
Zend Framework 2 Documentation, Release 2.3.1dev
18 ’text’ => ’ZEND-FRAMEWORK’,
19 ’font’ => 3
20 )
21 ); // will use the 3rd GD internal font
45.2 Common Additional Getters
Table 45.2: Common Getters
Getter Data
Type
Description
getType() String Return the name of the barcode class without the namespace (e.g.
ZendBarcodeObjectCode39 returns simply “code39”)
getRawText() String Return the original text provided to the object
getTextToDisplay() String Return the text to display, including, if activated, the checksum value
getQuietZone() Integer Return the size of the space needed before and after the barcode without any
drawing
getInstructions() Array Return drawing instructions as an array.
getH-
eight($recalculate =
false)
Integer Return the height of the barcode calculated after possible rotation
getWidth($recalculate
= false)
Integer Return the width of the barcode calculated after possible rotation
getOffset-
Top($recalculate =
false)
Integer Return the position of the top of the barcode calculated after possible rotation
getOff-
setLeft($recalculate =
false)
Integer Return the position of the left of the barcode calculated after possible rotation
orphan
45.3 Description of shipped barcodes
You will find below detailed information about all barcode types shipped by default with Zend Framework.
45.3.1 ZendBarcodeObjectError
This barcode is a special case. It is internally used to automatically render an exception caught by the ZendBarcode
component.
45.3.2 ZendBarcodeObjectCode128
• Name: Code 128
• Allowed characters: the complete ASCII-character set
45.2. Common Additional Getters 185
Zend Framework 2 Documentation, Release 2.3.1dev
• Checksum: optional (modulo 103)
• Length: variable
There are no particular options for this barcode.
45.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.
45.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.
45.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 45.3: ZendBarcodeObjectCode25interleaved Options
Option Data Type Default Value Description
withBearerBars Boolean FALSE Draw a thick bar at the top and the bottom of the barcode.
Note: If the number of characters is not even, ZendBarcodeObjectCode25interleaved will automati-
cally prepend the missing zero to the barcode text.
186 Chapter 45. ZendBarcodeBarcode Objects
Zend Framework 2 Documentation, Release 2.3.1dev
45.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.
45.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.
45.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.
45.3. Description of shipped barcodes 187
Zend Framework 2 Documentation, Release 2.3.1dev
45.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.
45.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.
45.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)
• 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.
188 Chapter 45. ZendBarcodeBarcode Objects
Zend Framework 2 Documentation, Release 2.3.1dev
45.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.
45.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)
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.
45.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.
45.3.15 ZendBarcodeObjectPostnet
• Name: Postnet (POSTal Numeric Encoding Technique)
45.3. Description of shipped barcodes 189
Zend Framework 2 Documentation, Release 2.3.1dev
• Allowed characters:‘0123456789’
• Checksum: mandatory (modulo 10)
• Length: 6, 7, 10 or 12 characters (including checksum)
There are no particular options for this barcode.
45.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.
45.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.
45.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)
190 Chapter 45. ZendBarcodeBarcode Objects
Zend Framework 2 Documentation, Release 2.3.1dev
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.
45.3. Description of shipped barcodes 191
Zend Framework 2 Documentation, Release 2.3.1dev
192 Chapter 45. ZendBarcodeBarcode Objects
CHAPTER 46
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 // Case 1
6 $renderer = new RendererPdf($options);
7
8 // Case 2
9 $renderer = new RendererPdf();
10 $renderer->setOptions($options);
11
12 // Case 3
13 $renderer = new RendererPdf();
14 $renderer->setTopOffset(10);
46.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:
193
Zend Framework 2 Documentation, Release 2.3.1dev
Table 46.1: Common Options
Option Data
Type
Default
Value
Description
render-
erNames-
pace
String ZendBarcodeRendererNamespace of the renderer; for example, if you need to extend the
renderers
horizon-
talPosi-
tion
String “left” Can be “left”, “center” or “right”. Can be useful with PDF or if the
setWidth() method is used with an image renderer.
vertical-
Position
String “top” Can be “top”, “middle” or “bottom”. Can be useful with PDF or if the
setHeight() method is used with an image renderer.
leftOffset Integer 0 Top position of the barcode inside the renderer. If used, this value will
override the “horizontalPosition” option.
topOffset Integer 0 Top position of the barcode inside the renderer. If used, this value will
override the “verticalPosition” option.
automati-
cRender-
Error
Boolean FALSE 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.
module-
Size
Float 1 Size of a rendering module in the support.
barcode ZendBarcodeObjectNULL 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”).
46.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 46.2: ZendBarcodeRendererImage Options
Option Data
Type
Default
Value
Description
height Integer 0 Allow you to specify the height of the result image. If “0”, the height will be
calculated by the barcode object.
width Integer 0 Allow you to specify the width of the result image. If “0”, the width will be
calculated by the barcode object.
im-
ageType
String “png” Specify the image format. Can be “png”, “jpeg”, “jpg” or “gif”.
46.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.
194 Chapter 46. ZendBarcode Renderers
CHAPTER 47
ZendCacheStorageAdapter
47.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 ex-
tend 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.
47.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 // Via factory:
4 $cache = StorageFactory::factory(array(
5 ’adapter’ => array(
6 ’name’ => ’apc’,
7 ’options’ => array(’ttl’ => 3600),
8 ),
195
Zend Framework 2 Documentation, Release 2.3.1dev
9 ’plugins’ => array(
10 ’exception_handler’ => array(’throw_exceptions’ => false),
11 ),
12 ));
13
14 // Alternately:
15 $cache = StorageFactory::adapterFactory(’apc’, array(’ttl’ => 3600));
16 $plugin = StorageFactory::pluginFactory(’exception_handler’, array(
17 ’throw_exceptions’ => false,
18 ));
19 $cache->addPlugin($plugin);
20
21 // Or manually:
22 $cache = new ZendCacheStorageAdapterApc();
23 $cache->getOptions()->setTtl(3600);
24
25 $plugin = new ZendCacheStoragePluginExceptionHandler();
26 $plugin->getOptions()->setThrowExceptions(false);
27 $cache->addPlugin($plugin);
47.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 Data Type Default Value Description
ttl integer 0 Time to live
namespace string “zfcache” The “namespace” in which cache items will live
key_pattern null‘‘|‘‘string null Pattern against which to validate cache keys
readable boolean true Enable/Disable reading data from cache
writable boolean true Enable/Disable writing data to cache
47.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
getItems(array $keys)
Load all items given by $keys returning key-value pairs.
196 Chapter 47. ZendCacheStorageAdapter
Zend Framework 2 Documentation, Release 2.3.1dev
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
touchItems(array $keys)
Reset lifetime of multiple items.
Return type boolean
47.4. The StorageInterface 197
Zend Framework 2 Documentation, Release 2.3.1dev
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
47.5 The AvailableSpaceCapableInterface
The ZendCacheStorageAvailableSpaceCapableInterface implements a method to make it possi-
ble getting the current available space of the storage.
getAvailableSpace()
Get available space in bytes.
Return type integer|float
47.6 The TotalSpaceCapableInterface
The ZendCacheStorageTotalSpaceCapableInterface implements a method to make it possible get-
ting the total space of the storage.
getTotalSpace()
Get total space in bytes.
Return type integer|float
198 Chapter 47. ZendCacheStorageAdapter
Zend Framework 2 Documentation, Release 2.3.1dev
47.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
47.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
47.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
47.10 The FlushableInterface
The ZendCacheStorageFlushableInterface implements a method to flush the complete storage.
flush()
Flush the whole storage.
Return type boolean
47.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
47.7. The ClearByNamespaceInterface 199
Zend Framework 2 Documentation, Release 2.3.1dev
47.12 The OptimizableInterface
The ZendCacheStorageOptimizableInterface implements a method to run optimization processes on
the storage.
optimize()
Optimize the storage.
Return type boolean
47.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
47.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
200 Chapter 47. ZendCacheStorageAdapter
Zend Framework 2 Documentation, Release 2.3.1dev
Table 47.1: Capabilities
Capability Value
supportedDatatypes null, boolean, integer, double, string, array (serialized), object (serialized)
supportedMetadata internal_key, atime, ctime, mtime, rtime, size, hits, ttl
minTtl 1
maxTtl 0
staticTtl true
ttlPrecision 1
useRequestTime <ini value of apc.use_request_time>
expiredRead false
maxKeyLength 5182
namespaceIsPrefix true
namespaceSeparator <Option value of namespace_separator>
Table 47.2: Adapter specific options
Name Data Type Default Value Description
namespace_separator string ”:” A separator for the namespace and prefix
47.15 The Dba Adapter
The ZendCacheStorageAdapterDba adapter stores cache items into dbm like databases us-
ing the required PHP extension dba.
This adapter implements the following interfaces:
• ZendCacheStorageStorageInterface
• ZendCacheStorageAvailableSpaceCapableInterface
• ZendCacheStorageClearByNamespaceInterface
• ZendCacheStorageClearByPrefixInterface
• ZendCacheStorageFlushableInterface
• ZendCacheStorageIterableInterface
• ZendCacheStorageOptimizableInterface
• ZendCacheStorageTotalSpaceCapableInterface
Table 47.3: Capabilities
Capability Value
supported-
Datatypes
string, null => string, boolean => string, integer => string, double =>
string
supportedMeta-
data
<none>
minTtl 0
maxKeyLength 0
namespaceIsPre-
fix
true
namespaceSepara-
tor
<Option value of namespace_separator>
47.15. The Dba Adapter 201
Zend Framework 2 Documentation, Release 2.3.1dev
Table 47.4: Adapter specific options
Name Data
Type
Default
Value
Description
names-
pace_separator
string ”:” A separator for the namespace and prefix
pathname string “” Pathname to the database file
mode string “c” The mode to open the database Please read dba_open for more
information
handler string “flatfile” 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.
47.16 The Filesystem Adapter
The ZendCacheStorageAdapterFilesystem adapter stores cache items into the filesys-
tem.
This adapter implements the following interfaces:
• ZendCacheStorageStorageInterface
• ZendCacheStorageAvailableSpaceCapableInterface
• ZendCacheStorageClearByNamespaceInterface
• ZendCacheStorageClearByPrefixInterface
• ZendCacheStorageClearExpiredInterface
• ZendCacheStorageFlushableInterface
• ZendCacheStorageIterableInterface
• ZendCacheStorageOptimizableInterface
• ZendCacheStorageTaggableInterface
• ZendCacheStorageTotalSpaceCapableInterface
202 Chapter 47. ZendCacheStorageAdapter
Zend Framework 2 Documentation, Release 2.3.1dev
Table 47.5: Capabilities
Capability Value
supported-
Datatypes
string, null => string, boolean => string, integer => string, double =>
string
supportedMeta-
data
mtime, filespec, atime, ctime
minTtl 1
maxTtl 0
staticTtl false
ttlPrecision 1
useRequestTime false
expiredRead true
maxKeyLength 251
namespaceIsPre-
fix
true
namespaceSepara-
tor
<Option value of namespace_separator>
Table 47.6: Adapter specific options
Name Data Type Default Value Description
names-
pace_separator
string ”:” A separator for the namespace and prefix
cache_dir string “” Directory to store cache files
clear_stat_cache boolean true Call clearstatcache() enabled?
dir_level integer 1 Defines how much sub-directories should be
created
dir_permission integer
false
0700 Set explicit permission on creating new
directories
file_locking boolean true Lock files on writing
file_permission integer
false
0600 Set explicit permission on creating new files
key_pattern string /^[a-z0-9_+-]*$/DiValidate key against pattern
no_atime boolean true Don’t get ‘fileatime’ as ‘atime’ on metadata
no_ctime boolean true Don’t get ‘filectime’ as ‘ctime’ on metadata
umask integer
false
false Use umask to set file and directory
permissions
47.17 The Memcached Adapter
The ZendCacheStorageAdapterMemcached adapter stores cache items over the mem-
cached protocol. It’s using the required PHP extension memcached which is based on Libmemcached.
This adapter implements the following interfaces:
• ZendCacheStorageStorageInterface
• ZendCacheStorageAvailableSpaceCapableInterface
• ZendCacheStorageFlushableInterface
• ZendCacheStorageTotalSpaceCapableInterface
47.17. The Memcached Adapter 203
Zend Framework 2 Documentation, Release 2.3.1dev
Table 47.7: Capabilities
Capability Value
supportedDatatypes null, boolean, integer, double, string, array (serialized), object (serialized)
supportedMetadata <none>
minTtl 1
maxTtl 0
staticTtl true
ttlPrecision 1
useRequestTime false
expiredRead false
maxKeyLength 255
namespaceIsPrefix true
namespaceSeparator <none>
Table 47.8: Adapter specific options
Name Data
Type
De-
fault
Value
Description
servers array [] List of servers in [] = array(string host, integer port)
lib_optionsarray [] 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
47.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
204 Chapter 47. ZendCacheStorageAdapter
Zend Framework 2 Documentation, Release 2.3.1dev
Table 47.9: Capabilities
Capability Value
supportedDatatypes string, null, boolean, integer, double, array, object, resource
supportedMetadata mtime
minTtl 1
maxTtl <Value of PHP_INT_MAX>
staticTtl false
ttlPrecision 0.05
useRequestTime false
expiredRead true
maxKeyLength 0
namespaceIsPrefix false
Table 47.10: Adapter specific options
Name Data Type Default Value Description
memory_limit string integer <50% of ini value
memory_limit>
Limit of how much memory
can PHP allocate to allow
store items into this adapter
• If the used mem-
ory of PHP exceeds
this limit an OutOf-
SpaceException 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.
47.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
47.19. The WinCache Adapter 205
Zend Framework 2 Documentation, Release 2.3.1dev
Table 47.11: Capabilities
Capability Value
supportedDatatypes null, boolean, integer, double, string, array (serialized), object (serialized)
supportedMetadata internal_key, ttl, hits, size
minTtl 1
maxTtl 0
staticTtl true
ttlPrecision 1
useRequestTime <ini value of apc.use_request_time>
expiredRead false
namespaceIsPrefix true
namespaceSeparator <Option value of namespace_separator>
Table 47.12: Adapter specific options
Name Data Type Default Value Description
namespace_separator string ”:” A separator for the namespace and prefix
47.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 47.13: Capabilities
Capability Value
supportedDatatypes boolean, integer, double, string, array (serialized), object (serialized)
supportedMetadata internal_key, size, refcount, hits, ctime, atime, hvalue
minTtl 1
maxTtl <ini value of xcache.var_maxttl>
staticTtl true
ttlPrecision 1
useRequestTime true
expiredRead false
maxKeyLength 5182
namespaceIsPrefix true
namespaceSeparator <Option value of namespace_separator>
206 Chapter 47. ZendCacheStorageAdapter
Zend Framework 2 Documentation, Release 2.3.1dev
Table 47.14: Adapter specific options
Name Data
Type
De-
fault
Value
Description
names-
pace_separator
string”:” A separator for the namespace and prefix
ad-
min_auth
booleanfalse 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.
ad-
min_user
string“” The username of xcache.admin.user
ad-
min_pass
string“” The password of xcache.admin.pass in plain text
47.21 The ZendServerDisk Adapter
This ZendCacheStorageAdapterZendServerDisk adapter stores cache items on filesys-
tem through the Zend Server Data Caching API.
This adapter implements the following interfaces:
• ZendCacheStorageStorageInterface
• ZendCacheStorageAvailableSpaceCapableInterface
• ZendCacheStorageClearByNamespaceInterface
• ZendCacheStorageFlushableInterface
• ZendCacheStorageTotalSpaceCapableInterface
Table 47.15: Capabilities
Capability Value
supportedDatatypes null, boolean, integer, double, string, array (serialized), object (serialized)
supportedMetadata <none>
minTtl 1
maxTtl 0
maxKeyLength 0
staticTtl true
ttlPrecision 1
useRequestTime false
expiredRead false
namespaceIsPrefix true
namespaceSeparator ::
47.22 The ZendServerShm Adapter
The ZendCacheStorageAdapterZendServerShm adapter stores cache items in shared
memory through the Zend Server Data Caching API.
This adapter implements the following interfaces:
47.21. The ZendServerDisk Adapter 207
Zend Framework 2 Documentation, Release 2.3.1dev
• ZendCacheStorageStorageInterface
• ZendCacheStorageClearByNamespaceInterface
• ZendCacheStorageFlushableInterface
• ZendCacheStorageTotalSpaceCapableInterface
Table 47.16: Capabilities
Capability Value
supportedDatatypes null, boolean, integer, double, string, array (serialized), object (serialized)
supportedMetadata <none>
minTtl 1
maxTtl 0
maxKeyLength 0
staticTtl true
ttlPrecision 1
useRequestTime false
expiredRead false
namespaceIsPrefix true
namespaceSeparator ::
47.23 Examples
Basic usage
1 $cache = ZendCacheStorageFactory::factory(array(
2 ’adapter’ => array(
3 ’name’ => ’filesystem’
4 ),
5 ’plugins’ => array(
6 // Don’t throw exceptions on cache errors
7 ’exception_handler’ => array(
8 ’throw_exceptions’ => false
9 ),
10 )
11 ));
12 $key = ’unique-cache-key’;
13 $result = $cache->getItem($key, $success);
14 if (!$success) {
15 $result = doExpensiveStuff();
16 $cache->setItem($key, $result);
17 }
Get multiple rows from db
1 // Instantiate the cache instance using a namespace for the same type of items
2 $cache = ZendCacheStorageFactory::factory(array(
3 ’adapter’ => array(
4 ’name’ => ’filesystem’
5 // With a namespace we can indicate the same type of items
6 // -> So we can simple use the db id as cache key
7 ’options’ => array(
208 Chapter 47. ZendCacheStorageAdapter
Zend Framework 2 Documentation, Release 2.3.1dev
8 ’namespace’ => ’dbtable’
9 ),
10 ),
11 ’plugins’ => array(
12 // Don’t throw exceptions on cache errors
13 ’exception_handler’ => array(
14 ’throw_exceptions’ => false
15 ),
16 // We store database rows on filesystem so we need to serialize them
17 ’Serializer’
18 )
19 ));
20
21 // Load two rows from cache if possible
22 $ids = array(1, 2);
23 $results = $cache->getItems($ids);
24 if (count($results) < count($ids)) {
25 // Load rows from db if loading from cache failed
26 $missingIds = array_diff($ids, array_keys($results));
27 $missingResults = array();
28 $query = ’SELECT * FROM dbtable WHERE id IN (’ . implode(’,’, $missingIds) . ’)’;
29 foreach ($pdo->query($query, PDO::FETCH_ASSOC) as $row) {
30 $missingResults[ $row[’id’] ] = $row;
31 }
32
33 // Update cache items of the loaded rows from db
34 $cache->setItems($missingResults);
35
36 // merge results from cache and db
37 $results = array_merge($results, $missingResults);
38 }
47.23. Examples 209
Zend Framework 2 Documentation, Release 2.3.1dev
210 Chapter 47. ZendCacheStorageAdapter
CHAPTER 48
ZendCacheStorageCapabilities
48.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.
48.2 Available Methods
__construct(ZendCacheStorageStorageInterface $storage, stdClass $marker, array $capabilities = ar-
ray(), 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
getMinTtl()
Get minimum supported time-to-live.
(Returning 0 means items never expire)
211
Zend Framework 2 Documentation, Release 2.3.1dev
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
setMaxKeyLength(stdClass $marker, int $maxKeyLength)
Set maximum key length.
Return type ZendCacheStorageCapabilities
212 Chapter 48. ZendCacheStorageCapabilities
Zend Framework 2 Documentation, Release 2.3.1dev
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
48.3 Examples
Get storage capabilities and do specific stuff in base of it
1 use ZendCacheStorageFactory;
2
3 $cache = StorageFactory::adapterFactory(’filesystem’);
4 $supportedDatatypes = $cache->getCapabilities()->getSupportedDatatypes();
5
6 // now you can run specific stuff in base of supported feature
7 if ($supportedDatatypes[’object’]) {
8 $cache->set($key, $object);
9 } else {
10 $cache->set($key, serialize($object));
11 }
Listen to change event
1 use ZendCacheStorageFactory;
2
3 $cache = StorageFactory::adapterFactory(’filesystem’, array(
4 ’no_atime’ => false,
5 ));
6
7 // Catching capability changes
8 $cache->getEventManager()->attach(’capability’, function($event) {
9 echo count($event->getParams()) . ’ capabilities changed’;
10 });
11
12 // change option which changes capabilities
13 $cache->getOptions()->setNoATime(true);
48.3. Examples 213
Zend Framework 2 Documentation, Release 2.3.1dev
214 Chapter 48. ZendCacheStorageCapabilities
CHAPTER 49
ZendCacheStoragePlugin
49.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 re-
sult (with setResult of ZendCacheStoragePostEvent) and catching exceptions (with
ZendCacheStorageExceptionEvent).
49.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 // Via factory:
4 $cache = StorageFactory::factory(array(
5 ’adapter’ => ’filesystem’,
6 ’plugins’ => array(’serializer’),
7 ));
8
9 // Alternately:
10 $cache = StorageFactory::adapterFactory(’filesystem’);
11 $plugin = StorageFactory::pluginFactory(’serializer’);
12 $cache->addPlugin($plugin);
13
14 // Or manually:
15 $cache = new ZendCacheStorageAdapterFilesystem();
16 $plugin = new ZendCacheStoragePluginSerializer();
17 $cache->addPlugin($plugin);
215
Zend Framework 2 Documentation, Release 2.3.1dev
49.3 The ClearExpiredByFactor Plugin
The ZendCacheStoragePluginClearExpiredByFactor plugin calls the storage method
clearExpired() randomly (by factor) after every call of setItem(), setItems(), addItem()
and addItems().
Table 49.1: Plugin specific options
Name Data Type Default Value Description
clearing_factor integer 0 The automatic clearing factor
Note: ** The ClearExpiredInterface is required **
The storage have to implement the ZendCacheStorageClearExpiredInterface to work with this plu-
gin.
49.4 The ExceptionHandler Plugin
The ZendCacheStoragePluginExceptionHandler 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 49.2: Plugin specific options
Name Data Type Default
Value
Description
excep-
tion_callback
callable
null
null Callback will be called on an exception and get the
exception as argument
throw_exceptions boolean true Re-throw catched exceptions
49.5 The IgnoreUserAbort Plugin
The ZendCacheStoragePluginIgnoreUserAbort plugin ignores script terminations by
users until write operations to cache finished.
Table 49.3: Plugin specific options
Name Data Type Default Value Description
exit_on_abort boolean true Terminate script execution if user abort the script
49.6 The OptimizeByFactor Plugin
The ZendCacheStoragePluginOptimizeByFactor plugin calls the storage method
optimize() randomly (by factor) after removing items from cache.
Table 49.4: Plugin specific options
Name Data Type Default Value Description
optimizing_factor integer 0 The automatic optimization factor
Note: ** The OptimizableInterface is required **
216 Chapter 49. ZendCacheStoragePlugin
Zend Framework 2 Documentation, Release 2.3.1dev
The storage have to implement the ZendCacheStorageOptimizableInterface to work with this plugin.
49.7 The Serializer Plugin
The ZendCacheStoragePluginSerializer 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 49.5: Plugin specific options
Name Data Type Default Value Description
serializer null string
ZendSerializerAdapterAdapterInterface
null The serializer to use
• If null use the de-
fault serializer
• If string instanti-
ate the serializer with
serializer_options
serializer_options array [] Array of serializer options
used to instantiate the seri-
alizer
49.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
49.9 Examples
Basics of writing an own storage plugin
49.7. The Serializer Plugin 217
Zend Framework 2 Documentation, Release 2.3.1dev
1 use ZendCacheStorageEvent;
2 use ZendCacheStoragePluginAbstractPlugin;
3 use ZendEventManagerEventManagerInterface;
4
5 class MyPlugin extends AbstractPlugin
6 {
7
8 protected $handles = array();
9
10 // This method have to attach all events required by this plugin
11 public function attach(EventManagerInterface $events)
12 {
13 $this->handles[] = $events->attach(’getItem.pre’, array($this, ’onGetItemPre’));
14 $this->handles[] = $events->attach(’getItem.post’, array($this, ’onGetItemPost’));
15 return $this;
16 }
17
18 // This method have to detach all events required by this plugin
19 public function detach(EventManagerInterface $events)
20 {
21 foreach ($this->handles as $handle) {
22 $events->detach($handle);
23 }
24 $this->handles = array();
25 return $this;
26 }
27
28 public function onGetItemPre(Event $event)
29 {
30 $params = $event->getParams();
31 echo sprintf("Method ’getItem’ with key ’%s’ startedn", params[’key’]);
32 }
33
34 public function onGetItemPost(Event $event)
35 {
36 $params = $event->getParams();
37 echo sprintf("Method ’getItem’ with key ’%s’ finishedn", params[’key’]);
38 }
39 }
40
41 // After defining this basic plugin we can instantiate and add it to an adapter instance
42 $plugin = new MyPlugin();
43 $cache->addPlugin($plugin);
44
45 // Now on calling getItem our basic plugin should print the expected output
46 $cache->getItem(’cache-key’);
47 // Method ’getItem’ with key ’cache-key’ started
48 // Method ’getItem’ with key ’cache-key’ finished
218 Chapter 49. ZendCacheStoragePlugin
CHAPTER 50
ZendCachePattern
50.1 Overview
Cache patterns are configurable objects to solve known performance bottlenecks. Each should be used only in the spe-
cific 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 ex-
tend the abstract class ZendCachePatternAbstractPattern to implement basic logic.
Configuration is provided via the ZendCachePatternPatternOptions class, which can simply be instan-
tiated 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.
50.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 // Via the factory:
2 $callbackCache = ZendCachePatternFactory::factory(’callback’, array(
3 ’storage’ => ’apc’,
4 ));
1 // OR, the equivalent manual instantiation:
2 $callbackCache = new ZendCachePatternCallbackCache();
3 $callbackCache->setOptions(new ZendCachePatternPatternOptions(array(
4 ’storage’ => ’apc’,
5 )));
50.3 Available Methods
The following methods are implemented by ZendCachePatternAbstractPattern. Please read docu-
mentation of specific patterns to get more information.
219
Zend Framework 2 Documentation, Release 2.3.1dev
setOptions(ZendCachePatternPatternOptions $options)
Set pattern options.
Return type ZendCachePatternPatternInterface
getOptions()
Get all pattern options.
Return type ZendCachePatternPatternOptions
220 Chapter 50. ZendCachePattern
CHAPTER 51
ZendCachePatternCallbackCache
51.1 Overview
The callback cache pattern caches calls of non specific functions and methods given as a callback.
51.2 Quick Start
For instantiation you can use the PatternFactory or do it manual:
1 use ZendCachePatternFactory;
2 use ZendCachePatternPatternOptions;
3
4 // Via the factory:
5 $callbackCache = PatternFactory::factory(’callback’, array(
6 ’storage’ => ’apc’,
7 ’cache_output’ => true,
8 ));
9
10 // OR, the equivalent manual instantiation:
11 $callbackCache = new ZendCachePatternCallbackCache();
12 $callbackCache->setOptions(new PatternOptions(array(
13 ’storage’ => ’apc’,
14 ’cache_output’ => true,
15 )));
51.3 Configuration Options
Option Data Type Default
Value
Description
storage string array
ZendCacheStorageStorageInterface
<none> The storage to write/read
cached data
cache_outputboolean true Cache output of callback
221
Zend Framework 2 Documentation, Release 2.3.1dev
51.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
51.5 Examples
Instantiating the callback cache pattern
1 use ZendCachePatternFactory;
2
3 $callbackCache = PatternFactory::factory(’callback’, array(
4 ’storage’ => ’apc’
5 ));
222 Chapter 51. ZendCachePatternCallbackCache
CHAPTER 52
ZendCachePatternClassCache
52.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.
52.2 Quick Start
Instantiating the class cache pattern
1 use ZendCachePatternFactory;
2
3 $classCache = PatternFactory::factory(’class’, array(
4 ’class’ => ’MyClass’,
5 ’storage’ => ’apc’
6 ));
52.3 Configuration Options
Option Data Type Default
Value
Description
storage string array
ZendCacheStorageStorageInterface
<none> The storage to write/read cached data
class string <none> The class name
cache_output boolean true Cache output of callback
cache_by_default boolean true Cache method calls by default
class_cache_methodsarray [] List of methods to cache (If
cache_by_default is disabled)
class_non_cache_methodsarray [] List of methods to no-cache (If
cache_by_default is enabled)
52.4 Available Methods
call(string $method, array $args = array())
223
Zend Framework 2 Documentation, Release 2.3.1dev
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
52.5 Examples
Caching of import feeds
1 $cachedFeedReader = ZendCachePatternFactory::factory(’class’, array(
2 ’class’ => ’ZendFeedReaderReader’,
3 ’storage’ => ’apc’,
4
5 // The feed reader doesn’t output anything
6 // so the output don’t need to be caught and cached
7 ’cache_output’ => false,
224 Chapter 52. ZendCachePatternClassCache
Zend Framework 2 Documentation, Release 2.3.1dev
8 ));
9
10 $feed = $cachedFeedReader->call("import", array(’http://www.planet-php.net/rdf/’));
11 // OR
12 $feed = $cachedFeedReader->import(’http://www.planet-php.net/rdf/’);
52.5. Examples 225
Zend Framework 2 Documentation, Release 2.3.1dev
226 Chapter 52. ZendCachePatternClassCache
CHAPTER 53
ZendCachePatternObjectCache
53.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.
53.2 Quick Start
Instantiating the object cache pattern
1 use ZendCachePatternFactory;
2
3 $object = new stdClass();
4 $objectCache = PatternFactory::factory(’object’, array(
5 ’object’ => $object,
6 ’storage’ => ’apc’
7 ));
53.3 Configuration Options
Option Data Type Default
Value
Description
storage string array
ZendCacheStorageStorageInterface
<none> The storage to write/read cached
data
object object <none> The object to cache methods calls of
object_key null string <Class name
of object>
A hopefully unique key of the object
cache_output boolean true Cache output of callback
cache_by_default boolean true Cache method calls by default
ob-
ject_cache_methods
array [] List of methods to cache (If
cache_by_default is disabled)
ob-
ject_non_cache_methods
array [] List of methods to no-cache (If
cache_by_default is enabled)
ob-
ject_cache_magic_properties
boolean false Cache calls of magic object
properties
227
Zend Framework 2 Documentation, Release 2.3.1dev
53.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
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
53.5 Examples
Caching a filter
228 Chapter 53. ZendCachePatternObjectCache
Zend Framework 2 Documentation, Release 2.3.1dev
1 $filter = new ZendFilterRealPath();
2 $cachedFilter = ZendCachePatternFactory::factory(’object’, array(
3 ’object’ => $filter,
4 ’object_key’ => ’RealpathFilter’,
5 ’storage’ => ’apc’,
6
7 // The realpath filter doesn’t output anything
8 // so the output don’t need to be caught and cached
9 ’cache_output’ => false,
10 ));
11
12 $path = $cachedFilter->call("filter", array(’/www/var/path/../../mypath’));
13 // OR
14 $path = $cachedFilter->filter(’/www/var/path/../../mypath’);
53.5. Examples 229
Zend Framework 2 Documentation, Release 2.3.1dev
230 Chapter 53. ZendCachePatternObjectCache
CHAPTER 54
ZendCachePatternOutputCache
54.1 Overview
The OutputCache pattern caches output between calls to start() and end().
54.2 Quick Start
Instantiating the output cache pattern
1 use ZendCachePatternFactory;
2
3 $outputCache = PatternFactory::factory(’output’, array(
4 ’storage’ => ’apc’
5 ));
54.3 Configuration Options
Op-
tion
Data Type Default
Value
Description
stor-
age
string array
ZendCacheStorageStorageInterface
<none> The storage to write/read
cached data
54.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.
Return type boolean
setOptions(ZendCachePatternPatternOptions $options)
Set pattern options.
231
Zend Framework 2 Documentation, Release 2.3.1dev
Return type ZendCachePatternOutputCache
getOptions()
Get all pattern options.
Return type ZendCachePatternPatternOptions
54.5 Examples
Caching simple view scripts
1 $outputCache = ZendCachePatternFactory::factory(’output’, array(
2 ’storage’ => ’apc’,
3 ));
4
5 $outputCache->start(’mySimpleViewScript’);
6 include ’/path/to/view/script.phtml’;
7 $outputCache->end();
232 Chapter 54. ZendCachePatternOutputCache
CHAPTER 55
ZendCachePatternCaptureCache
55.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.
55.2 Quick Start
Simplest usage as Apache-404 handler
1 # .htdocs
2 ErrorDocument 404 /index.php
1 // index.php
2 use ZendCachePatternFactory;
3 $capture = ZendCachePatternFactory::factory(’capture’, array(
4 ’public_dir’ => __DIR__,
5 ));
6
7 // Start capturing all output excl. headers and write to public directory
8 $capture->start();
9
10 // Don’t forget to change HTTP response code
11 header(’Status: 200’, true, 200);
12
13 // do stuff to dynamically generate output
233
Zend Framework 2 Documentation, Release 2.3.1dev
55.3 Configuration Options
Option Data Type Default Value Description
public_dir string <none> Location of public directory to write output to
index_filename string “index.html” The name of the first file if only a directory was requested
file_locking boolean true Locking output files on writing
file_permission integer boolean 0600 (false on win) Set permissions of generated output files
dir_permission integer boolean 0700 (false on win) Set permissions of generated output directories
umask integer boolean false Using umask on generating output files / directories
55.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
234 Chapter 55. ZendCachePatternCaptureCache
Zend Framework 2 Documentation, Release 2.3.1dev
55.5 Examples
Scaling images in base of request
1 # .htdocs
2 ErrorDocument 404 /index.php
1 // index.php
2 $captureCache = ZendCachePatternFactory::factory(’capture’, array(
3 ’public_dir’ => __DIR__,
4 ));
5
6 // TODO
55.5. Examples 235
Zend Framework 2 Documentation, Release 2.3.1dev
236 Chapter 55. ZendCachePatternCaptureCache
CHAPTER 56
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 multi-
ple 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.
237
Zend Framework 2 Documentation, Release 2.3.1dev
238 Chapter 56. Introduction to ZendCaptcha
CHAPTER 57
Captcha Operation
All CAPTCHA adapters implement ZendCaptchaAdapterInterface, which looks like the following:
1 namespace ZendCaptcha;
2
3 use ZendValidatorValidatorInterface;
4
5 interface AdapterInterface extends ValidatorInterface
6 {
7 public function generate();
8
9 public function setName($name);
10
11 public function getName();
12
13 // Get helper name used for rendering this captcha type
14 public function getHelperName();
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 // Originating request:
2 $captcha = new ZendCaptchaFiglet(array(
3 ’name’ => ’foo’,
4 ’wordLen’ => 6,
5 ’timeout’ => 300,
6 ));
7
8 $id = $captcha->generate();
9
10 //this will output a Figlet string
11 echo $captcha->getFiglet()->render($captcha->getWord());
12
13
14 // On a subsequent request:
15 // Assume a captcha setup as before, with corresponding form fields, the value of $_POST[’foo’]
16 // would be key/value array: id => captcha ID, input => captcha value
17 if ($captcha->isValid($_POST[’foo’], $_POST)) {
18 // Validated!
239
Zend Framework 2 Documentation, Release 2.3.1dev
19 }
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.
240 Chapter 57. Captcha Operation
CHAPTER 58
CAPTCHA Adapters
The following adapters are shipped with Zend Framework by default.
58.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 interface,
ZendCaptchaAbstractWord exposes the following methods:
• 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 consid-
ered 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.
Note: ZendCaptchaAbstractWord is an abstract class and may not be instantiated directly.
241
Zend Framework 2 Documentation, Release 2.3.1dev
58.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.
58.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 docu-
mentation for details on what configuration options are available.
58.4 ZendCaptchaImage
The ZendCaptchaImage adapter takes the generated word and renders it as an image, performing various skew-
ing 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 im-
ages. 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.
• 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
242 Chapter 58. CAPTCHA Adapters
Zend Framework 2 Documentation, Release 2.3.1dev
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.
58.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.
58.5. ZendCaptchaReCaptcha 243
Zend Framework 2 Documentation, Release 2.3.1dev
244 Chapter 58. CAPTCHA Adapters
CHAPTER 59
Introduction
ZendCodeGenerator provides facilities to generate arbitrary code using an object-oriented interface, both to
create new code as well as to update existing code. While the current implementation is limited to generating PHP
code, you can easily extend the base class in order to provide code generation for other tasks: JavaScript, configuration
files, apache vhosts, etc.
59.1 Theory of Operation
In the most typical use case, you will simply instantiate a code generator class and either pass it the appropriate
configuration or configure it after instantiation. To generate the code, you will simply echo the object or call its
generate() method.
1 // Passing configuration to the constructor:
2 $file = new ZendCodeGeneratorFileGenerator(array(
3 ’classes’ => array(
4 new ZendCodeGeneratorClassGenerator(array(
5 ’name’ => ’World’,
6 ’methods’ => array(
7 new ZendCodeGeneratorMethodGenerator(array(
8 ’name’ => ’hello’,
9 ’body’ => ’echo ’Hello world!’;’,
10 )),
11 ),
12 )),
13 )
14 ));
15
16 // Configuring after instantiation
17 $method = new ZendCodeGeneratorMethodGenerator();
18 $method->setName(’hello’)
19 ->setBody(’echo ’Hello world!’;’);
20
21 $class = new ZendCodeGeneratorClassGenerator();
22 $class->setName(’World’)
23 ->setMethod($method);
24
25 $file = new ZendCodeGeneratorFileGenerator();
26 $file->setClass($class);
27
28 // Render the generated file
29 echo $file;
245
Zend Framework 2 Documentation, Release 2.3.1dev
30
31 // or write it to a file:
32 file_put_contents(’World.php’, $file->generate());
Both of the above samples will render the same result:
1 <?php
2
3 class World
4 {
5
6 public function hello()
7 {
8 echo ’Hello world!’;
9 }
10
11 }
Another common use case is to update existing code – for instance, to add a method to a class. In such a case, you must
first inspect the existing code using reflection, and then add your new method. ZendCodeGenerator makes this
trivially simple, by leveraging ZendCodeReflection.
As an example, let’s say we’ve saved the above to the file World.php, and have already included it. We could then
do the following:
1 $class = ZendCodeGeneratorClassGenerator::fromReflection(
2 new ZendCodeReflectionClassReflection(’World’)
3 );
4
5 $method = new ZendCodeGeneratorMethodGenerator();
6 $method->setName(’mrMcFeeley’)
7 ->setBody(’echo ’Hello, Mr. McFeeley!’;’);
8 $class->setMethod($method);
9
10 $file = new ZendCodeGeneratorFileGenerator();
11 $file->setClass($class);
12
13 // Render the generated file
14 echo $file;
15
16 // Or, better yet, write it back to the original file:
17 file_put_contents(’World.php’, $file->generate());
The resulting class file will now look like this:
1 <?php
2
3 class World
4 {
5
6 public function hello()
7 {
8 echo ’Hello world!’;
9 }
10
11 public function mrMcFeeley()
12 {
13 echo ’Hellow Mr. McFeeley!’;
14 }
246 Chapter 59. Introduction
Zend Framework 2 Documentation, Release 2.3.1dev
15
16 }
59.1. Theory of Operation 247
Zend Framework 2 Documentation, Release 2.3.1dev
248 Chapter 59. Introduction
CHAPTER 60
ZendCodeGenerator Reference
60.1 Abstract Classes and Interfaces
60.1.1 ZendCodeGeneratorGeneratorInterface
The base interface from which all CodeGenerator classes implement provides the minimal functionality necessary. It’s
API is as follows:
1 interface ZendCodeGeneratorGeneratorInterface
2 {
3 public function generate();
4 }
60.1.2 ZendCodeGeneratorAbstractGenerator
ZendCodeGeneratorAbstractGenerator implements ZendCodeGeneratorGeneratorInterface,
and adds some properties for tracking whether content has changed as well as the amount of indentation that should
appear before generated content. Its API is as follows:
1 abstract class ZendCodeGeneratorAbstractGenerator
2 implements ZendCodeGeneratorGeneratorInterface
3 {
4 public function __construct(Array|Traversable $options = array())
5 public function setOptions(Array $options)
6 public function setSourceContent($sourceContent)
7 public function getSourceContent()
8 public function setSourceDirty($isSourceDirty = true)
9 public function isSourceDirty()
10 public function setIndentation($indentation)
11 public function getIndentation()
12 }
The constructor passes the $options parameter to setOptions().
Like most classes in Zend Framework, setOptions() compares an option key to existing setters in the class, and
passes the value on to that method if found.
setSourceContent() and getSourceContent() are intended to either set the default content for the code
being generated, or to replace said content once all generation tasks are complete.
249
Zend Framework 2 Documentation, Release 2.3.1dev
60.1.3 ZendCodeGeneratorAbstractMemberGenerator
ZendCodeGeneratorAbstractMemberGenerator is a base class for generating class members – prop-
erties and methods – and provides accessors and mutators for establishing visibility; whether or not the member is
abstract, static, or final; and the name of the member. Its API is as follows:
1 abstract class ZendCodeGeneratorAbstractMemberGenerator
2 extends ZendCodeGeneratorAbstractGenerator
3 {
4 public function setAbstract($isAbstract)
5 public function isAbstract()
6 public function setStatic($isStatic)
7 public function isStatic()
8 public function setVisibility($visibility)
9 public function getVisibility()
10 public function setName($name)
11 public function getName()
12 }
60.2 Concrete CodeGenerator Classes
60.2.1 ZendCodeGeneratorBodyGenerator
ZendCodeGeneratorBodyGenerator is intended for generating arbitrary procedural code to include
within a file. As such, you simply set content for the object, and it will return that content when you invoke
generate().
The API of the class is as follows:
1 class ZendCodeGeneratorBodyGenerator extends ZendCodeGeneratorAbstractGenerator
2 {
3 public function setContent($content)
4 public function getContent()
5 public function generate()
6 }
60.2.2 ZendCodeGeneratorClassGenerator
ZendCodeGeneratorClassGenerator is intended for generating PHP classes. The basic functionality
just generates the PHP class itself, as well as optionally the related PHP DocBlock. Classes may implement or inherit
from other classes, and may be marked as abstract. Utilizing other code generator classes, you can also attach class
constants, properties, and methods.
The API is as follows:
1 class ZendCodeGeneratorClassGenerator extends ZendCodeGeneratorAbstractGenerator
2 {
3 public static function fromReflection(
4 ZendCodeReflectionClassReflection $reflectionClass
5 )
6 public function setDocblock(ZendCodeGeneratorDocBlockGenerator $docblock)
7 public function getDocblock()
8 public function setName($name)
9 public function getName()
10 public function setAbstract($isAbstract)
250 Chapter 60. ZendCodeGenerator Reference
Zend Framework 2 Documentation, Release 2.3.1dev
11 public function isAbstract()
12 public function setExtendedClass($extendedClass)
13 public function getExtendedClass()
14 public function setImplementedInterfaces(Array $implementedInterfaces)
15 public function getImplementedInterfaces()
16 public function setProperties(Array $properties)
17 public function setProperty($property)
18 public function getProperties()
19 public function getProperty($propertyName)
20 public function setMethods(Array $methods)
21 public function setMethod($method)
22 public function getMethods()
23 public function getMethod($methodName)
24 public function hasMethod($methodName)
25 public function isSourceDirty()
26 public function generate()
27 }
The setProperty() method accepts an array of information that may be used to gener-
ate a ZendCodeGeneratorPropertyGenerator instance – or simply an instance of
ZendCodeGeneratorPropertyGenerator. Likewise, setMethod() accepts either an array of
information for generating a ZendCodeGeneratorMethodGenerator instance or a concrete instance of
that class.
Note that setDocBlock() expects an instance of ZendCodeGeneratorDocBlockGenerator.
60.2.3 ZendCodeGeneratorDocBlockGenerator
ZendCodeGeneratorDocBlockGenerator can be used to generate arbitrary PHP docblocks, including
all the standard docblock features: short and long descriptions and annotation tags.
Annotation tags may be set using the setTag() and setTags() methods; these each take either an array describing
the tag that may be passed to the ZendCodeGeneratorDocBlockTag constructor, or an instance of that
class.
The API is as follows:
1 class ZendCodeGeneratorDocBlockGenerator extends ZendCodeGeneratorAbstractGenerator
2 {
3 public static function fromReflection(
4 ZendCodeReflectionDocblockReflection $reflectionDocblock
5 )
6 public function setShortDescription($shortDescription)
7 public function getShortDescription()
8 public function setLongDescription($longDescription)
9 public function getLongDescription()
10 public function setTags(Array $tags)
11 public function setTag($tag)
12 public function getTags()
13 public function generate()
14 }
60.2.4 ZendCodeGeneratorDocBlockTag
ZendCodeGeneratorDocBlockTag is intended for creating arbitrary annotation tags for inclusion in PHP
docblocks. Tags are expected to contain a name (the portion immediately following the ‘@’ symbol) and a description
60.2. Concrete CodeGenerator Classes 251
Zend Framework 2 Documentation, Release 2.3.1dev
(everything following the tag name).
The class API is as follows:
1 class ZendCodeGeneratorDocBlockTag
2 extends ZendCodeGeneratorAbstractGenerator
3 {
4 public static function fromReflection(
5 ZendCodeReflectionDocBlockTagTagInterface $reflectionTag
6 )
7 public function setName($name)
8 public function getName()
9 public function setDescription($description)
10 public function getDescription()
11 public function generate()
12 }
60.2.5 ZendCodeGeneratorDocBlockTagParamTag
ZendCodeGeneratorDocBlockTagParamTag is a specialized version of
ZendCodeGeneratorDocBlockTag, and represents a method parameter. The tag name is therefor
known (“param”), but due to the format of this annotation tag, additional information is required in order to generate
it: the parameter name and data type it represents.
The class API is as follows:
1 class ZendCodeGeneratorDocBlockTagParamTag
2 extends ZendCodeGeneratorDocBlockTag
3 {
4 public static function fromReflection(
5 ZendCodeReflectionDocBlockTagTagInterface $reflectionTagParam
6 )
7 public function setDatatype($datatype)
8 public function getDatatype()
9 public function setParamName($paramName)
10 public function getParamName()
11 public function generate()
12 }
60.2.6 ZendCodeGeneratorDocBlockTagReturnTag
Like the param docblock tag variant, ZendCodeGeneratorDocBlockTagReturnTag is an annotation
tag variant for representing a method return value. In this case, the annotation tag name is known (“return”), but
requires a return type.
The class API is as follows:
1 class ZendCodeGeneratorDocBlockTagParamTag
2 extends ZendCodeGeneratorDocBlockTag
3 {
4 public static function fromReflection(
5 ZendCodeReflectionDocBlockTagTagInterface $reflectionTagReturn
6 )
7 public function setDatatype($datatype)
8 public function getDatatype()
9 public function generate()
10 }
252 Chapter 60. ZendCodeGenerator Reference
Zend Framework 2 Documentation, Release 2.3.1dev
60.2.7 ZendCodeGeneratorFileGenerator
ZendCodeGeneratorFileGenerator is used to generate the full contents of a file that will contain PHP
code. The file may contain classes or arbitrary PHP code, as well as a file-level docblock if desired.
When adding classes to the file, you will need to pass either an array of information to pass to the
ZendCodeGeneratorClassGenerator constructor, or an instance of that class. Similarly, with docblocks,
you will need to pass information for the ZendCodeGeneratorDocBlockGenerator constructor to con-
sume or an instance of the class.
The API of the class is as follows:
1 class ZendCodeGeneratorFileGenerator extends ZendCodeGeneratorAbstractGenerator
2 {
3 public static function fromReflectedFilePath(
4 $filePath,
5 $usePreviousCodeGeneratorIfItExists = true,
6 $includeIfNotAlreadyIncluded = true)
7 public static function fromReflection(ZendCodeReflectionFileReflection $reflectionFile)
8 public function setDocblock(ZendCodeGeneratorDocBlockGenerator $docblock)
9 public function getDocblock()
10 public function setRequiredFiles($requiredFiles)
11 public function getRequiredFiles()
12 public function setClasses(Array $classes)
13 public function getClass($name = null)
14 public function setClass($class)
15 public function setFilename($filename)
16 public function getFilename()
17 public function getClasses()
18 public function setBody($body)
19 public function getBody()
20 public function isSourceDirty()
21 public function generate()
22 }
60.2.8 ZendCodeGeneratorMemberContainerGenerator
ZendCodeGeneratorMemberContainerGenerator is used internally by
ZendCodeGeneratorClassGenerator to keep track of class members – properties and methods
alike. These are indexed by name, using the concrete instances of the members as values.
The API of the class is as follows:
1 class ZendCodeGeneratorMemberContainerGenerator extends ArrayObject
2 {
3 public function __construct($type = self::TYPE_PROPERTY)
4 }
60.2.9 ZendCodeGeneratorMethodGenerator
ZendCodeGeneratorMethodGenerator describes a class method, and can generate both the code and
the docblock for the method. The visibility and status as static, abstract, or final may be indicated, per its parent
class, ZendCodeGeneratorAbstractMemberGenerator. Finally, the parameters and return value for
the method may be specified.
60.2. Concrete CodeGenerator Classes 253
Zend Framework 2 Documentation, Release 2.3.1dev
Parameters may be set using setParameter() or setParameters(). In each case, a parameter should either
be an array of information to pass to the ZendCodeGeneratorParameterGenerator constructor or an
instance of that class.
The API of the class is as follows:
1 class ZendCodeGeneratorMethodGenerator
2 extends ZendCodeGeneratorAbstractMemberGenerator
3 {
4 public static function fromReflection(
5 ZendCodeReflectionMethodReflection $reflectionMethod
6 )
7 public function setDocblock(ZendCodeGeneratorDocBlockGenerator $docblock)
8 public function getDocblock()
9 public function setFinal($isFinal)
10 public function setParameters(Array $parameters)
11 public function setParameter($parameter)
12 public function getParameters()
13 public function setBody($body)
14 public function getBody()
15 public function generate()
16 }
60.2.10 ZendCodeGeneratorParameterGenerator
ZendCodeGeneratorParameterGenerator may be used to specify method parameters. Each parameter
may have a position (if unspecified, the order in which they are registered with the method will be used), a default
value, and a data type; a parameter name is required.
The API of the class is as follows:
1 class ZendCodeGeneratorParameterGenerator extends ZendCodeGeneratorAbstractGenerator
2 {
3 public static function fromReflection(
4 ZendCodeReflectionParameterReflection $reflectionParameter
5 )
6 public function setType($type)
7 public function getType()
8 public function setName($name)
9 public function getName()
10 public function setDefaultValue($defaultValue)
11 public function getDefaultValue()
12 public function setPosition($position)
13 public function getPosition()
14 public function getPassedByReference()
15 public function setPassedByReference($passedByReference)
16 public function generate()
17 }
There are several problems that might occur when trying to set NULL, booleans or arrays as default values. For this
the value holder object ZendCodeGeneratorParameterDefaultValueGenerator can be used, for
example:
1 $parameter = new ZendCodeGeneratorParameterGenerator();
2 $parameter->setDefaultValue(
3 new ZendCodeGeneratorValueGenerator("null")
4 );
5 $parameter->setDefaultValue(
254 Chapter 60. ZendCodeGenerator Reference
Zend Framework 2 Documentation, Release 2.3.1dev
6 new ZendCodeGeneratorValueGenerator("array(’foo’, ’bar’)")
7 );
Internally setDefaultValue() also converts the values which can’t be expressed in PHP into the value holder.
60.2.11 ZendCodeGeneratorPropertyGenerator
ZendCodeGeneratorPropertyGenerator describes a class property, which may be either a constant or a
variable. In each case, the property may have an optional default value associated with it. Additionally, the visibility of
variable properties may be set, per the parent class, ZendCodeGeneratorAbstractMemberGenerator.
The API of the class is as follows:
1 class ZendCodeGeneratorPropertyGenerator
2 extends ZendCodeGeneratorAbstractMemberGenerator
3 {
4 public static function fromReflection(
5 ZendCodeReflectionPropertyReflection $reflectionProperty
6 )
7 public function setConst($const)
8 public function isConst()
9 public function setDefaultValue($defaultValue)
10 public function getDefaultValue()
11 public function generate()
12 }
60.2. Concrete CodeGenerator Classes 255
Zend Framework 2 Documentation, Release 2.3.1dev
256 Chapter 60. ZendCodeGenerator Reference
CHAPTER 61
ZendCodeGenerator Examples
Generating PHP classes
The following example generates an empty class with a class-level DocBlock.
1 use ZendCodeGeneratorClassGenerator;
2 use ZendCodeGeneratorDocBlockGenerator;
3
4 $foo = new ClassGenerator();
5 $docblock = DocBlockGenerator::fromArray(array(
6 ’shortDescription’ => ’Sample generated class’,
7 ’longDescription’ => ’This is a class generated with ZendCodeGenerator.’,
8 ’tags’ => array(
9 array(
10 ’name’ => ’version’,
11 ’description’ => ’$Rev:$’,
12 ),
13 array(
14 ’name’ => ’license’,
15 ’description’ => ’New BSD’,
16 ),
17 ),
18 ));
19 $foo->setName(’Foo’)
20 ->setDocblock($docblock);
21 echo $foo->generate();
The above code will result in the following:
1 /**
2 * Sample generated class
3 *
4 * This is a class generated with ZendCodeGenerator.
5 *
6 * @version $Rev:$
7 * @license New BSD
8 *
9 */
10 class Foo
11 {
12
13 }
257
Zend Framework 2 Documentation, Release 2.3.1dev
Generating PHP classes with class properties
Building on the previous example, we now add properties to our generated class.
1 use ZendCodeGeneratorClassGenerator;
2 use ZendCodeGeneratorDocBlockGenerator;
3 use ZendCodeGeneratorPropertyGenerator;
4
5 $foo = new ClassGenerator();
6 $docblock = DocBlockGenerator::fromArray(array(
7 ’shortDescription’ => ’Sample generated class’,
8 ’longDescription’ => ’This is a class generated with ZendCodeGenerator.’,
9 ’tags’ => array(
10 array(
11 ’name’ => ’version’,
12 ’description’ => ’$Rev:$’,
13 ),
14 array(
15 ’name’ => ’license’,
16 ’description’ => ’New BSD’,
17 ),
18 ),
19 ));
20 $foo->setName(’Foo’)
21 ->setDocblock($docblock)
22 ->setProperties(array(
23 array(’_bar’, ’baz’, PropertyGenerator::FLAG_PROTECTED),
24 array(’baz’, ’bat’, PropertyGenerator::FLAG_PUBLIC),
25 array(’bat’, ’foobarbazbat’, PropertyGenerator::FLAG_CONSTANT),
26 ));
27 echo $foo->generate();
The above results in the following class definition:
1 /**
2 * Sample generated class
3 *
4 * This is a class generated with ZendCodeGenerator.
5 *
6 * @version $Rev:$
7 * @license New BSD
8 *
9 */
10 class Foo
11 {
12
13 protected $_bar = ’baz’;
14
15 public $baz = ’bat’;
16
17 const bat = ’foobarbazbat’;
18
19 }
258 Chapter 61. ZendCodeGenerator Examples
Zend Framework 2 Documentation, Release 2.3.1dev
Generating PHP classes with class methods
ZendCodeGeneratorClassGenerator allows you to attach methods with optional content to your classes.
Methods may be attached as either arrays or concrete ZendCodeGeneratorMethodGenerator instances.
1 use ZendCodeGeneratorClassGenerator;
2 use ZendCodeGeneratorDocBlockGenerator;
3 use ZendCodeGeneratorDocBlockTag;
4 use ZendCodeGeneratorMethodGenerator;
5 use ZendCodeGeneratorPropertyGenerator;
6
7 $foo = new ClassGenerator();
8 $docblock = DocBlockGenerator::fromArray(array(
9 ’shortDescription’ => ’Sample generated class’,
10 ’longDescription’ => ’This is a class generated with ZendCodeGenerator.’,
11 ’tags’ => array(
12 array(
13 ’name’ => ’version’,
14 ’description’ => ’$Rev:$’,
15 ),
16 array(
17 ’name’ => ’license’,
18 ’description’ => ’New BSD’,
19 ),
20 ),
21 ));
22 $foo->setName(’Foo’)
23 ->setDocblock($docblock)
24 ->addProperties(array(
25 array(’_bar’, ’baz’, PropertyGenerator::FLAG_PROTECTED),
26 array(’baz’, ’bat’, PropertyGenerator::FLAG_PUBLIC),
27 array(’bat’, ’foobarbazbat’, PropertyGenerator::FLAG_CONSTANT),
28 ))
29 ->addMethods(array(
30 // Method passed as array
31 MethodGenerator::fromArray(array(
32 ’name’ => ’setBar’,
33 ’parameters’ => array(’bar’),
34 ’body’ => ’$this->_bar = $bar;’ . "n" . ’return $this;’,
35 ’docblock’ => DocBlockGenerator::fromArray(array(
36 ’shortDescription’ => ’Set the bar property’,
37 ’longDescription’ => null,
38 ’tags’ => array(
39 new TagParamTag(array(
40 ’paramName’ => ’bar’,
41 ’datatype’ => ’string’
42 )),
43 new TagReturnTag(array(
44 ’datatype’ => ’string’,
45 )),
46 ),
47 )),
48 )),
49 // Method passed as concrete instance
50 new MethodGenerator(
51 ’getBar’,
52 array(),
53 MethodGenerator::FLAG_PUBLIC,
259
Zend Framework 2 Documentation, Release 2.3.1dev
54 ’return $this->_bar;’,
55 DocBlockGenerator::fromArray(array(
56 ’shortDescription’ => ’Retrieve the bar property’,
57 ’longDescription’ => null,
58 ’tags’ => array(
59 new TagReturnTag(array(
60 ’datatype’ => ’string|null’,
61 )),
62 ),
63 ))
64 ),
65 ));
66
67 echo $foo->generate();
The above generates the following output:
1 /**
2 * Sample generated class
3 *
4 * This is a class generated with ZendCodeGenerator.
5 *
6 * @version $Rev:$
7 * @license New BSD
8 */
9 class Foo
10 {
11
12 protected $_bar = ’baz’;
13
14 public $baz = ’bat’;
15
16 const bat = ’foobarbazbat’;
17
18 /**
19 * Set the bar property
20 *
21 * @param string bar
22 * @return string
23 */
24 public function setBar($bar)
25 {
26 $this->_bar = $bar;
27 return $this;
28 }
29
30 /**
31 * Retrieve the bar property
32 *
33 * @return string|null
34 */
35 public function getBar()
36 {
37 return $this->_bar;
38 }
39
40 }
260 Chapter 61. ZendCodeGenerator Examples
Zend Framework 2 Documentation, Release 2.3.1dev
Generating PHP files
ZendCodeGeneratorFileGenerator can be used to generate the contents of a PHP file. You can
include classes as well as arbitrary content body. When attaching classes, you should attach either concrete
ZendCodeGeneratorClassGenerator instances or an array defining the class.
In the example below, we will assume you’ve defined $foo per one of the class definitions in a previous example.
1 use ZendCodeGeneratorDocBlockGenerator;
2 use ZendCodeGeneratorFileGenerator;
3
4 $file = FileGenerator::fromArray(array(
5 ’classes’ => array($foo),
6 ’docblock’ => DocBlockGenerator::fromArray(array(
7 ’shortDescription’ => ’Foo class file’,
8 ’longDescription’ => null,
9 ’tags’ => array(
10 array(
11 ’name’ => ’license’,
12 ’description’ => ’New BSD’,
13 ),
14 ),
15 )),
16 ’body’ => ’define(’APPLICATION_ENV’, ’testing’);’,
17 ));
Calling generate() will generate the code – but not write it to a file. You will need to capture the contents and
write them to a file yourself. As an example:
1 $code = $file->generate();
2 file_put_contents(’Foo.php’, $code);
The above will generate the following file:
1 <?php
2 /**
3 * Foo class file
4 *
5 * @license New BSD
6 */
7
8 /**
9 * Sample generated class
10 *
11 * This is a class generated with ZendCodeGenerator.
12 *
13 * @version $Rev:$
14 * @license New BSD
15 */
16 class Foo
17 {
18
19 protected $_bar = ’baz’;
20
21 public $baz = ’bat’;
22
23 const bat = ’foobarbazbat’;
24
25 /**
261
Zend Framework 2 Documentation, Release 2.3.1dev
26 * Set the bar property
27 *
28 * @param string bar
29 * @return string
30 */
31 public function setBar($bar)
32 {
33 $this->_bar = $bar;
34 return $this;
35 }
36
37 /**
38 * Retrieve the bar property
39 *
40 * @return string|null
41 */
42 public function getBar()
43 {
44 return $this->_bar;
45 }
46
47 }
48
49 define(’APPLICATION_ENV’, ’testing’);
Seeding PHP file code generation via reflection
You can add PHP code to an existing PHP file using the code generator. To do so, you need to first do reflection on it.
The static method fromReflectedFileName() allows you to do this.
1 $generator = ZendCodeGeneratorFileGenerator::fromReflectedFileName($path);
2 $body = $generator->getBody();
3 $body .= "n$foo->bar();";
4 file_put_contents($path, $generator->generate());
Seeding PHP class generation via reflection
You may add code to an existing class. To do so, first use the static fromReflection() method to map the class
into a generator object. From there, you may add additional properties or methods, and then regenerate the class.
1 use ZendCodeGeneratorClassGenerator;
2 use ZendCodeGeneratorDocBlockGenerator;
3 use ZendCodeGeneratorDocBlockTag;
4 use ZendCodeGeneratorMethodGenerator;
5 use ZendCodeReflectionClassReflection;
6
7 $generator = ClassGenerator::fromReflection(
8 new ClassReflection($class)
9 );
10 $generator->addMethod(
11 ’setBaz’,
12 array(’baz’),
13 MethodGenerator::FLAG_PUBLIC,
14 ’$this->_baz = $baz;’ . "n" . ’return $this;’,
15 DocBlockGenerator::fromArray(array(
262 Chapter 61. ZendCodeGenerator Examples
Zend Framework 2 Documentation, Release 2.3.1dev
16 ’shortDescription’ => ’Set the baz property’,
17 ’longDescription’ => null,
18 ’tags’ => array(
19 new TagParamTag(array(
20 ’paramName’ => ’baz’,
21 ’datatype’ => ’string’
22 )),
23 new TagReturnTag(array(
24 ’datatype’ => ’string’,
25 )),
26 ),
27 ))
28 );
29 $code = $generator->generate();
263
Zend Framework 2 Documentation, Release 2.3.1dev
264 Chapter 61. ZendCodeGenerator Examples
CHAPTER 62
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.
62.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 // An array of configuration data is given
2 $configArray = array(
3 ’webhost’ => ’www.example.com’,
4 ’database’ => array(
5 ’adapter’ => ’pdo_mysql’,
6 ’params’ => array(
7 ’host’ => ’db.example.com’,
8 ’username’ => ’dbuser’,
9 ’password’ => ’secret’,
10 ’dbname’ => ’mydatabase’
11 )
12 )
13 );
14
15 // Create the object-oriented wrapper using the configuration data
16 $config = new ZendConfigConfig($configArray);
17
18 // Print a configuration datum (results in ’www.example.com’)
19 echo $config->webhost;
As illustrated in the example above, ZendConfigConfig provides nested object property syntax to access con-
figuration 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:
1 $host = $config->database->get(’host’, ’localhost’);
265
Zend Framework 2 Documentation, Release 2.3.1dev
62.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 // config.php
2 return array(
3 ’webhost’ => ’www.example.com’,
4 ’database’ => array(
5 ’adapter’ => ’pdo_mysql’,
6 ’params’ => array(
7 ’host’ => ’db.example.com’,
8 ’username’ => ’dbuser’,
9 ’password’ => ’secret’,
10 ’dbname’ => ’mydatabase’
11 )
12 )
13 );
1 // Consumes the configuration array
2 $config = new ZendConfigConfig(include ’config.php’);
3
4 // Print a configuration datum (results in ’www.example.com’)
5 echo $config->webhost;
266 Chapter 62. Introduction to ZendConfig
CHAPTER 63
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 configu-
ration 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 config-
uration 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 behav-
ior 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() func-
tion. 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.
267
Zend Framework 2 Documentation, Release 2.3.1dev
268 Chapter 63. Theory of Operation
CHAPTER 64
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.
64.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 $reader = new ZendConfigReaderIni();
2 $reader->setNestSeparator(’-’);
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:
269
Zend Framework 2 Documentation, Release 2.3.1dev
1 webhost = ’www.example.com’
2 database.adapter = ’pdo_mysql’
3 database.params.host = ’db.example.com’
4 database.params.username = ’dbuser’
5 database.params.password = ’secret’
6 database.params.dbname = ’dbproduction’
We can use the ZendConfigReaderIni to read this INI file:
1 $reader = new ZendConfigReaderIni();
2 $data = $reader->fromFile(’/path/to/config.ini’);
3
4 echo $data[’webhost’] // prints "www.example.com"
5 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 database.adapter = ’pdo_mysql’
2 database.params.host = ’db.example.com’
3 database.params.username = ’dbuser’
4 database.params.password = ’secret’
5 database.params.dbname = ’dbproduction’
We can include this configuration in another INI file, for instance:
1 webhost = ’www.example.com’
2 @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 adapter = ’pdo_mysql’
2 params.host = ’db.example.com’
3 params.username = ’dbuser’
4 params.password = ’secret’
5 params.dbname = ’dbproduction’
And assign the @include as subelement of the database value:
1 webhost = ’www.example.com’
2 database.@include = ’database.ini’
64.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.
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 <?xml version="1.0" encoding="utf-8"?>?>
2 <config>
3 <webhost>www.example.com</webhost>
270 Chapter 64. ZendConfigReader
Zend Framework 2 Documentation, Release 2.3.1dev
4 <database>
5 <adapter value="pdo_mysql"/>
6 <params>
7 <host value="db.example.com"/>
8 <username value="dbuser"/>
9 <password value="secret"/>
10 <dbname value="dbproduction"/>
11 </params>
12 </database>
13 </config>
We can use the ZendConfigReaderXml to read this XML file:
1 $reader = new ZendConfigReaderXml();
2 $data = $reader->fromFile(’/path/to/config.xml’);
3
4 echo $data[’webhost’] // prints "www.example.com"
5 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 <?xml version="1.0" encoding="utf-8"?>
2 <config>
3 <database>
4 <adapter>pdo_mysql</adapter>
5 <params>
6 <host>db.example.com</host>
7 <username>dbuser</username>
8 <password>secret</password>
9 <dbname>dbproduction</dbname>
10 </params>
11 </database>
12 </config>
We can include this configuration in another XML file, for instance:
1 <?xml version="1.0" encoding="utf-8"?>
2 <config xmlns:xi="http://www.w3.org/2001/XInclude">
3 <webhost>www.example.com</webhost>
4 <xi:include href="database.xml"/>
5 </config>
The syntax to include an XML file in a specific element is <xi:include href="file-to-include.xml"/>
64.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:
64.3. ZendConfigReaderJson 271
Zend Framework 2 Documentation, Release 2.3.1dev
1 {
2 "webhost" : "www.example.com",
3 "database" : {
4 "adapter" : "pdo_mysql",
5 "params" : {
6 "host" : "db.example.com",
7 "username" : "dbuser",
8 "password" : "secret",
9 "dbname" : "dbproduction"
10 }
11 }
12 }
We can use the ZendConfigReaderJson to read this JSON file:
1 $reader = new ZendConfigReaderJson();
2 $data = $reader->fromFile(’/path/to/config.json’);
3
4 echo $data[’webhost’] // prints "www.example.com"
5 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 {
2 "database" : {
3 "adapter" : "pdo_mysql",
4 "params" : {
5 "host" : "db.example.com",
6 "username" : "dbuser",
7 "password" : "secret",
8 "dbname" : "dbproduction"
9 }
10 }
11 }
We can include this configuration in another JSON file, for instance:
1 {
2 "webhost" : "www.example.com",
3 "@include" : "database.json"
4 }
64.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 webhost: www.example.com
2 database:
272 Chapter 64. ZendConfigReader
Zend Framework 2 Documentation, Release 2.3.1dev
3 adapter: pdo_mysql
4 params:
5 host: db.example.com
6 username: dbuser
7 password: secret
8 dbname: dbproduction
We can use the ZendConfigReaderYaml to read this YAML file:
1 $reader = new ZendConfigReaderYaml();
2 $data = $reader->fromFile(’/path/to/config.yaml’);
3
4 echo $data[’webhost’] // prints "www.example.com"
5 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 // include the Spyc library
2 require_once (’path/to/spyc.php’);
3
4 $reader = new ZendConfigReaderYaml(array(’Spyc’,’YAMLLoadString’));
5 $data = $reader->fromFile(’/path/to/config.yaml’);
6
7 echo $data[’webhost’] // prints "www.example.com"
8 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 database:
2 adapter: pdo_mysql
3 params:
4 host: db.example.com
5 username: dbuser
6 password: secret
7 dbname: dbproduction
We can include this configuration in another YAML file, for instance:
webhost: www.example.com
@include: database.yaml
64.4. ZendConfigReaderYaml 273
Zend Framework 2 Documentation, Release 2.3.1dev
274 Chapter 64. ZendConfigReader
CHAPTER 65
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
65.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 de-
fines 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 // Create the config object
2 $config = new ZendConfigConfig(array(), true);
3 $config->production = array();
4
5 $config->production->webhost = ’www.example.com’;
6 $config->production->database = array();
7 $config->production->database->params = array();
8 $config->production->database->params->host = ’localhost’;
9 $config->production->database->params->username = ’production’;
275
Zend Framework 2 Documentation, Release 2.3.1dev
10 $config->production->database->params->password = ’secret’;
11 $config->production->database->params->dbname = ’dbproduction’;
12
13 $writer = new ZendConfigWriterIni();
14 echo $writer->toString($config);
The result of this code is an INI string contains the following values:
1 [production]
2 webhost = "www.example.com"
3 database.params.host = "localhost"
4 database.params.username = "production"
5 database.params.password = "secret"
6 database.params.dbname = "dbproduction"
You can use the method toFile() to store the INI data in a file.
65.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 // Create the config object
2 $config = new ZendConfigConfig(array(), true);
3 $config->production = array();
4
5 $config->production->webhost = ’www.example.com’;
6 $config->production->database = array();
7 $config->production->database->params = array();
8 $config->production->database->params->host = ’localhost’;
9 $config->production->database->params->username = ’production’;
10 $config->production->database->params->password = ’secret’;
11 $config->production->database->params->dbname = ’dbproduction’;
12
13 $writer = new ZendConfigWriterXml();
14 echo $writer->toString($config);
The result of this code is an XML string contains the following data:
1 <?xml version="1.0" encoding="UTF-8"?>
2 <zend-config>
3 <production>
4 <webhost>www.example.com</webhost>
5 <database>
6 <params>
7 <host>localhost</host>
8 <username>production</username>
9 <password>secret</password>
10 <dbname>dbproduction</dbname>
11 </params>
12 </database>
276 Chapter 65. ZendConfigWriter
Zend Framework 2 Documentation, Release 2.3.1dev
13 </production>
14 </zend-config>
You can use the method toFile() to store the XML data in a file.
65.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 // Create the config object
2 $config = new ZendConfigConfig(array(), true);
3 $config->production = array();
4
5 $config->production->webhost = ’www.example.com’;
6 $config->production->database = array();
7 $config->production->database->params = array();
8 $config->production->database->params->host = ’localhost’;
9 $config->production->database->params->username = ’production’;
10 $config->production->database->params->password = ’secret’;
11 $config->production->database->params->dbname = ’dbproduction’;
12
13 $writer = new ZendConfigWriterPhpArray();
14 echo $writer->toString($config);
The result of this code is a PHP script that returns an array as follow:
1 <?php
2 return array (
3 ’production’ =>
4 array (
5 ’webhost’ => ’www.example.com’,
6 ’database’ =>
7 array (
8 ’params’ =>
9 array (
10 ’host’ => ’localhost’,
11 ’username’ => ’production’,
12 ’password’ => ’secret’,
13 ’dbname’ => ’dbproduction’,
14 ),
15 ),
16 ),
17 );
You can use the method toFile() to store the PHP script in a file.
65.3. ZendConfigWriterPhpArray 277
Zend Framework 2 Documentation, Release 2.3.1dev
65.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 // Create the config object
2 $config = new ZendConfigConfig(array(), true);
3 $config->production = array();
4
5 $config->production->webhost = ’www.example.com’;
6 $config->production->database = array();
7 $config->production->database->params = array();
8 $config->production->database->params->host = ’localhost’;
9 $config->production->database->params->username = ’production’;
10 $config->production->database->params->password = ’secret’;
11 $config->production->database->params->dbname = ’dbproduction’;
12
13 $writer = new ZendConfigWriterJson();
14 echo $writer->toString($config);
The result of this code is a JSON string contains the following values:
1 { "webhost" : "www.example.com",
2 "database" : {
3 "params" : {
4 "host" : "localhost",
5 "username" : "production",
6 "password" : "secret",
7 "dbname" : "dbproduction"
8 }
9 }
10 }
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.
65.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:
278 Chapter 65. ZendConfigWriter
Zend Framework 2 Documentation, Release 2.3.1dev
1 // Create the config object
2 $config = new ZendConfigConfig(array(), true);
3 $config->production = array();
4
5 $config->production->webhost = ’www.example.com’;
6 $config->production->database = array();
7 $config->production->database->params = array();
8 $config->production->database->params->host = ’localhost’;
9 $config->production->database->params->username = ’production’;
10 $config->production->database->params->password = ’secret’;
11 $config->production->database->params->dbname = ’dbproduction’;
12
13 $writer = new ZendConfigWriterYaml();
14 echo $writer->toString($config);
The result of this code is a YAML string contains the following values:
1 webhost: www.example.com
2 database:
3 params:
4 host: localhost
5 username: production
6 password: secret
7 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 // include the Spyc library
2 require_once (’path/to/spyc.php’);
3
4 $writer = new ZendConfigWriterYaml(array(’Spyc’,’YAMLDump’));
5 echo $writer->toString($config);
65.5. ZendConfigWriterYaml 279
Zend Framework 2 Documentation, Release 2.3.1dev
280 Chapter 65. ZendConfigWriter
CHAPTER 66
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: translate configuration values in other languages using
ZendI18nTranslator;
Below we reported some examples for each type of processor.
66.1 ZendConfigProcessorConstant
Using ZendConfigProcessorConstant
This example illustrates the basic use of ZendConfigProcessorConstant:
1 define (’TEST_CONST’, ’bar’);
2 // set true to ZendConfigConfig to allow modifications
3 $config = new ZendConfigConfig(array(’foo’ => ’TEST_CONST’), true);
4 $processor = new ZendConfigProcessorConstant();
5
6 echo $config->foo . ’,’;
7 $processor->process($config);
8 echo $config->foo;
This example returns the output: TEST_CONST, bar..
66.2 ZendConfigProcessorFilter
Using ZendConfigProcessorFilter
This example illustrates the basic use of ZendConfigProcessorFilter:
281
Zend Framework 2 Documentation, Release 2.3.1dev
1 use ZendFilterStringToUpper;
2 use ZendConfigProcessorFilter as FilterProcessor;
3 use ZendConfigConfig;
4
5 $config = new Config(array (’foo’ => ’bar’), true);
6 $upper = new StringToUpper();
7
8 $upperProcessor = new FilterProcessor($upper);
9
10 echo $config->foo . ’,’;
11 $upperProcessor->process($config);
12 echo $config->foo;
This example returns the output: bar,BAR.
66.3 ZendConfigProcessorQueue
Using ZendConfigProcessorQueue
This example illustrates the basic use of ZendConfigProcessorQueue:
1 use ZendFilterStringToLower;
2 use ZendFilterStringToUpper;
3 use ZendConfigProcessorFilter as FilterProcessor;
4 use ZendConfigProcessorQueue;
5 use ZendConfigConfig;
6
7 $config = new Config(array (’foo’ => ’bar’), true);
8 $upper = new StringToUpper();
9 $lower = new StringToLower();
10
11 $lowerProcessor = new FilterProcessor($lower);
12 $upperProcessor = new FilterProcessor($upper);
13
14 $queue = new Queue();
15 $queue->insert($upperProcessor);
16 $queue->insert($lowerProcessor);
17 $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).
66.4 ZendConfigProcessorToken
Using ZendConfigProcessorToken
This example illustrates the basic use of ZendConfigProcessorToken:
1 // set the Config to true to allow modifications
2 $config = new Config(array(’foo’ => ’Value is TOKEN’), true);
3 $processor = new TokenProcessor();
4
282 Chapter 66. ZendConfigProcessor
Zend Framework 2 Documentation, Release 2.3.1dev
5 $processor->addToken(’TOKEN’, ’bar’);
6 echo $config->foo . ’,’;
7 $processor->process($config);
8 echo $config->foo;
This example returns the output: Value is TOKEN,Value is bar.
66.5 ZendConfigProcessorTranslator
Using ZendConfigProcessorTranslator
This example illustrates the basic use of ZendConfigProcessorTranslator:
1 use ZendConfigConfig;
2 use ZendConfigProcessorTranslator as TranslatorProcessor;
3 use ZendI18nTranslatorTranslator;
4
5 $config = new Config(array(’animal’ => ’dog’), true);
6
7 /*
8 * The following mapping would exist for the translation
9 * loader you provide to the translator instance
10 * $italian = array(
11 * ’dog’ => ’cane’
12 * );
13 */
14
15 $translator = new Translator();
16 // ... configure the translator ...
17 $processor = new TranslatorProcessor($translator);
18
19 echo "English: {$config->animal}, ";
20 $processor->process($config);
21 echo "Italian: {$config->animal}";
This example returns the output: English: dog, Italian: cane.
66.5. ZendConfigProcessorTranslator 283
Zend Framework 2 Documentation, Release 2.3.1dev
284 Chapter 66. ZendConfigProcessor
CHAPTER 67
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 configu-
rations 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.
67.1 Loading configuration file
The next example illustrates how to load a single configuration file
1 //Load a php file as array
2 $config = ZendConfigFactory::fromFile(__DIR__.’/config/my.config.php’);
3
4 //Load a xml file as Config object
5 $config = ZendConfigFactory::fromFile(__DIR__.’/config/my.config.xml’, true);
For merging multiple configuration files
67.2 Storing configuration file
Sometimes you want to store the configuration to a file. Also this is really easy to do
285
Zend Framework 2 Documentation, Release 2.3.1dev
286 Chapter 67. The Factory
CHAPTER 68
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.
68.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>
This simple route definition expects exactly 3 arguments: a literal “user”, literal “resetpassword” followed by a pa-
rameter we’re calling “userEmail”. Let’s assume we also accept one optional parameter, that will turn on verbose
operation:
287
Zend Framework 2 Documentation, Release 2.3.1dev
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 array(
2 ’router’ => array(
3 ’routes’ => array(
4 // HTTP routes are defined here
5 )
6 ),
7
8 ’console’ => array(
9 ’router’ => array(
10 ’routes’ => array(
11 // Console routes go here
12 )
13 )
14 ),
15 )
Let’s create our console route and point it to ApplicationControllerIndexController::resetpasswordAction()
1 // we could define routes for ApplicationControllerIndexController in Application module config fil
2 // which is usually located at modules/application/config/module.config.php
3 array(
4 ’console’ => array(
5 ’router’ => array(
6 ’routes’ => array(
7 ’user-reset-password’ => array(
8 ’options’ => array(
9 ’route’ => ’user resetpassword [--verbose|-v] <userEmail>’,
10 ’defaults’ => array(
11 ’controller’ => ’ApplicationControllerIndex’,
12 ’action’ => ’password’
13 )
14 )
15 )
16 )
17 )
18 )
19 )
See also:
To learn more about console routes and how to use them, please read this chapter: Console routes and routing
288 Chapter 68. Introduction to ZendConsole
Zend Framework 2 Documentation, Release 2.3.1dev
68.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 <?php
2 namespace ApplicationController;
3
4 use ZendMvcControllerAbstractActionController;
5 use ZendViewModelViewModel;
6 use ZendConsoleRequest as ConsoleRequest;
7 use ZendMathRand;
8
9 class IndexController extends AbstractActionController
10 {
11 public function indexAction()
12 {
13 return new ViewModel(); // display standard index page
14 }
15
16 public function resetpasswordAction()
17 {
18 $request = $this->getRequest();
19
20 // Make sure that we are running in a console and the user has not tricked our
21 // application into running this action from a public web server.
22 if (!$request instanceof ConsoleRequest){
23 throw new RuntimeException(’You can only use this action from a console!’);
24 }
25
26 // Get user email from console and check if the user used --verbose or -v flag
27 $userEmail = $request->getParam(’userEmail’);
28 $verbose = $request->getParam(’verbose’);
29
30 // reset new password
31 $newPassword = Rand::getString(16);
32
33 // Fetch the user and change his password, then email him ...
34 // [...]
35
36 if (!$verbose) {
37 return "Done! $userEmail has received an email with his new password.n";
38 }else{
39 return "Done! New password for user $userEmail is ’$newPassword’. It has also been emaile
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.
68.2. Handling console requests 289
Zend Framework 2 Documentation, Release 2.3.1dev
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
68.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 use ZendModuleManagerFeatureAutoloaderProviderInterface;
6 use ZendModuleManagerFeatureConfigProviderInterface;
7 use ZendModuleManagerFeatureConsoleUsageProviderInterface;
8 use ZendConsoleAdapterAdapterInterface as Console;
9
10 class Module implements
11 AutoloaderProviderInterface,
12 ConfigProviderInterface,
13 ConsoleUsageProviderInterface // <- our module implement this feature and provides console usag
14 {
15 public function getConfig()
16 {
17 // [...]
18 }
19
20 public function getAutoloaderConfig()
21 {
22 // [...]
23 }
24
25 public function getConsoleUsage(Console $console)
26 {
27 return array(
28 // Describe available commands
29 ’user resetpassword [--verbose|-v] EMAIL’ => ’Reset password for a user’,
30
31 // Describe expected parameters
32 array( ’EMAIL’, ’Email of the user for a password reset’ ),
33 array( ’--verbose|-v’, ’(optional) turn on verbose mode’ ),
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.
Note: The order of usage info displayed in the console is the order modules load. If you want your application to
290 Chapter 68. Introduction to ZendConsole
Zend Framework 2 Documentation, Release 2.3.1dev
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
68.3. Adding console usage info 291
Zend Framework 2 Documentation, Release 2.3.1dev
292 Chapter 68. Introduction to ZendConsole
CHAPTER 69
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.
69.1 Router configuration
All Console Routes are automatically read from the following configuration location:
1 // This can sit inside of modules/Application/config/module.config.php or any other module’s config.
2 array(
3 ’router’ => array(
4 ’routes’ => array(
5 // HTTP routes are here
6 )
7 ),
8
9 ’console’ => array(
10 ’router’ => array(
11 ’routes’ => array(
12 // Console routes go here
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:
1 // inside config.console.router.routes:
2 // [...]
293
Zend Framework 2 Documentation, Release 2.3.1dev
3 ’my-first-route’ => array(
4 ’type’ => ’simple’, // <- simple route is created by default, we can skip that
5 ’options’ => array(
6 ’route’ => ’foo bar’,
7 ’defaults’ => array(
8 ’controller’ => ’ApplicationControllerIndex’,
9 ’action’ => ’password’
10 )
11 )
12 )
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.
69.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])
69.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 ’show-users’ => array(
2 ’options’ => array(
3 ’route’ => ’show users’,
4 ’defaults’ => array(
5 ’controller’ => ’ApplicationControllerUsers’,
6 ’action’ => ’show’
7 )
8 )
9 )
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:
1 ’show-users’ => array(
2 ’options’ => array(
3 ’route’ => ’show [all] users’,
294 Chapter 69. Console routes and routing
Zend Framework 2 Documentation, Release 2.3.1dev
4 ’defaults’ => array(
5 ’controller’ => ’ApplicationControllerUsers’,
6 ’action’ => ’show’
7 )
8 )
9 )
Now this route will match for both of these commands:
> zf show users
> zf show all users
We can also provide parameter alternative:
1 ’show-users’ => array(
2 ’options’ => array(
3 ’route’ => ’show [all|deleted|locked|admin] users’,
4 ’defaults’ => array(
5 ’controller’ => ’ApplicationControllerUsers’,
6 ’action’ => ’show’
7 )
8 )
9 )
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
69.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 ’check-users’ => array(
2 ’options’ => array(
3 ’route’ => ’check users [--verbose] [--fast] [--thorough]’,
4 ’defaults’ => array(
5 ’controller’ => ’ApplicationControllerUsers’,
6 ’action’ => ’check’
7 )
8 )
9 )
This route will match for commands like:
> zf check users
> zf check users --fast
69.2. Basic route 295
Zend Framework 2 Documentation, Release 2.3.1dev
> zf check users --verbose --thorough
> zf check users --thorough --fast
We can also define one or more mandatory long flags and group them as an alternative:
1 ’check-users’ => array(
2 ’options’ => array(
3 ’route’ => ’check users (--suspicious|--expired) [--verbose] [--fast] [--thorough]’,
4 ’defaults’ => array(
5 ’controller’ => ’ApplicationControllerUsers’,
6 ’action’ => ’check’
7 )
8 )
9 )
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 ’check-users’ => array(
2 ’options’ => array(
3 ’route’ => ’check users [--verbose|-v] [--fast|-f] [--thorough|-t]’,
4 ’defaults’ => array(
5 ’controller’ => ’ApplicationControllerUsers’,
6 ’action’ => ’check’
7 )
8 )
9 )
Now we can use short versions of our flags:
> zf check users -f
> zf check users -v --thorough
> zf check users -t -f -v
69.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 ’delete-user’ => array(
2 ’options’ => array(
3 ’route’ => ’delete user <userEmail>’,
4 ’defaults’ => array(
5 ’controller’ => ’ApplicationControllerUsers’,
6 ’action’ => ’delete’
7 )
8 )
9 )
This route will match for commands like:
296 Chapter 69. Console routes and routing
Zend Framework 2 Documentation, Release 2.3.1dev
> 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 ’delete-user’ => array(
2 ’options’ => array(
3 ’route’ => ’delete user [<userEmail>]’,
4 ’defaults’ => array(
5 ’controller’ => ’ApplicationControllerUsers’,
6 ’action’ => ’delete’
7 )
8 )
9 )
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 ’create-user’ => array(
2 ’options’ => array(
3 ’route’ => ’create user <firstName> <lastName> <email> <position>’,
4 ’defaults’ => array(
5 ’controller’ => ’ApplicationControllerUsers’,
6 ’action’ => ’create’
7 )
8 )
9 )
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"
69.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 ’find-user’ => array(
2 ’options’ => array(
3 ’route’ => ’find user [--id=] [--firstName=] [--lastName=] [--email=] [--position=] ’,
4 ’defaults’ => array(
5 ’controller’ => ’ApplicationControllerUsers’,
6 ’action’ => ’find’
7 )
69.2. Basic route 297
Zend Framework 2 Documentation, Release 2.3.1dev
8 )
9 )
This route will match for any of the following routes:
> zf find user
> zf find user --id 29110
> zf find user --id=29110
> zf find user --firstName=Johny --lastName=Bravo
> zf find user --lastName Bravo --firstName Johny
> zf find user --position=Executive --firstName=Bob
> zf find user --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 ’rename-user’ => array(
2 ’options’ => array(
3 ’route’ => ’rename user --id= [--firstName=] [--lastName=]’,
4 ’defaults’ => array(
5 ’controller’ => ’ApplicationControllerUsers’,
6 ’action’ => ’rename’
7 )
8 )
9 )
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
69.3 Catchall route
This special route will catch all console requests, regardless of the parameters provided.
1 ’default-route’ => array(
2 ’type’ => ’catchall’,
3 ’options’ => array(
4 ’route’ => ’’,
5 ’defaults’ => array(
6 ’controller’ => ’ApplicationControllerIndex’,
7 ’action’ => ’consoledefault’
8 )
9 )
10 )
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.
298 Chapter 69. Console routes and routing
Zend Framework 2 Documentation, Release 2.3.1dev
69.4 Console routes cheat-sheet
Param type Example route definition Explanation
Literal params
Literal foo bar “foo” followed by “bar”
Literal alternative foo (bar|baz) “foo” followed by “bar” or “baz”
Literal, optional foo [bar] “foo”, optional “bar”
Literal, optional
alternative
foo [bar|baz] “foo”, optional “bar” or “baz”
Flags
Flag long foo --bar “foo” as first parameter, “–bar” flag before or after
Flag long, optional foo [--bar] “foo” as first parameter, optional “–bar” flag before or
after
Flag long, optional,
alternative
foo [--bar|--baz] “foo” as first parameter, optional “–bar” or “–baz”,
before or after
Flag short foo -b “foo” as first parameter, “-b” flag before or after
Flag short, optional foo [-b] “foo” as first parameter, optional “-b” flag before or after
Flag short, optional,
alternative
foo [-b|-z] “foo” as first parameter, optional “-b” or “-z”, before or
after
Flag long/short
alternative
foo [--bar|-b] “foo” as first parameter, optional “–bar” or “-b” before
or after
Value parameters
Value positional param foo <bar> “foo” followed by any text (stored as “bar” param)
Value positional param,
optional
foo [<bar>] “foo”, optionally followed by any text (stored as “bar”
param)
Value Flag foo --bar= “foo” as first parameter, “–bar” with a value, before or
after
Value Flag, optional foo [--bar=] “foo” as first parameter, optionally “–bar” with a value,
before or after
Parameter groups
Literal params group foo
(bar|baz):myParam
“foo” followed by “bar” or “baz” (stored as “myParam”
param)
Literal optional params
group
foo
[bar|baz]:myParam
“foo” followed by optional “bar” or “baz” (stored as
“myParam” param)
Long flags group foo
(--bar|--baz):myParam
“foo”, “bar” or “baz” flag before or after (stored as
“myParam” param)
Long optional flags
group
foo
[--bar|--baz]:myParam
“foo”, optional “bar” or “baz” flag before or after (as
“myParam” param)
Short flags group foo
(-b|-z):myParam
“foo”, “-b” or “-z” flag before or after (stored as
“myParam” param)
Short optional flags
group
foo
[-b|-z]:myParam
“foo”, optional “-b” or “-z” flag before or after (stored
as “myParam” param)
69.4. Console routes cheat-sheet 299
Zend Framework 2 Documentation, Release 2.3.1dev
300 Chapter 69. Console routes and routing
CHAPTER 70
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.
70.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.
1 // modules/Application/Module.php
2 <?php
3 namespace Application;
4
5 use ZendModuleManagerFeatureConsoleBannerProviderInterface;
6 use ZendConsoleAdapterAdapterInterface as Console;
7
8 class Module implements ConsoleBannerProviderInterface
9 {
10 /**
11 * This method is defined in ConsoleBannerProviderInterface
12 */
13 public function getConsoleBanner(Console $console)
14 {
15 return ’MyModule 0.0.1’;
16 }
17 }
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.
301
Zend Framework 2 Documentation, Release 2.3.1dev
After running our application, we’ll see our newly created banner.
Let’s create and load second module that provides a banner.
1 <?php
2 // config/application.config.php
3 return array(
4 ’modules’ => array(
5 ’Application’,
6 ’User’, // < load user module in modules/User
7 ),
User module will add-on a short info about itself:
1 // modules/User/Module.php
2 <?php
3 namespace User;
4
5 use ZendModuleManagerFeatureConsoleBannerProviderInterface;
6 use ZendConsoleAdapterAdapterInterface as Console;
7
8 class Module implements ConsoleBannerProviderInterface
9 {
10 /**
11 * This method is defined in ConsoleBannerProviderInterface
12 */
13 public function getConsoleBanner(Console $console){
14 return "User Module 0.0.1";
15 }
16 }
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.
70.2 Basic usage
In order to display usage information, our Module class has to implement
ZendModuleManagerFeatureConsoleUsageProviderInterface. Let’s modify our example
and add new method:
1 // modules/Application/Module.php
2 <?php
3 namespace Application;
4
5 use ZendModuleManagerFeatureConsoleBannerProviderInterface;
6 use ZendModuleManagerFeatureConsoleUsageProviderInterface;
7 use ZendConsoleAdapterAdapterInterface as Console;
8
9 class Module implements ConsoleBannerProviderInterface, ConsoleUsageProviderInterface
10 {
11 public function getConsoleBanner(Console $console){ // ... }
302 Chapter 70. Console-aware modules
Zend Framework 2 Documentation, Release 2.3.1dev
12
13 /**
14 * This method is defined in ConsoleUsageProviderInterface
15 */
16 public function getConsoleUsage(Console $console)
17 {
18 return array(
19 ’show stats’ => ’Show application statistics’,
20 ’run cron’ => ’Run automated jobs’,
21 ’(enable|disable) debug’ => ’Enable or disable debug mode for the application.’
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
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.
70.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 public function getConsoleUsage(Console $console)
2 {
3 return ’User module expects exactly one argument - user name. It will display information for thi
4 }
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().
70.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 public function getConsoleUsage(Console $console)
2 {
3 return array(
4 ’delete user <userEmail>’ => ’Delete user with email <userEmail>’,
5 ’disable user <userEmail>’ => ’Disable user with email <userEmail>’,
70.2. Basic usage 303
Zend Framework 2 Documentation, Release 2.3.1dev
6 ’list [all|disabled] users’ => ’Show a list of users’,
7 ’find user [--email=] [--name=]’ => ’Attempt to find a user by email or name’,
8 );
9 }
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.
70.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:
1 public function getConsoleUsage(Console $console)
2 {
3 return array(
4 array( ’<userEmail>’ , ’email of the user’ ),
5 array( ’--verbose’ , ’Turn on verbose mode’ ),
6 array( ’--quick’ , ’Perform a "quick" operation’ ),
7 array( ’-v’ , ’Same as --verbose’ ),
8 array( ’-w’ , ’Wide output’)
9 );
10 }
Using this method, we can display more than 2 columns of information, for example:
1 public function getConsoleUsage(Console $console)
2 {
3 return array(
4 array( ’<userEmail>’ , ’user email’ , ’Full email address of the user to find.’ ),
5 array( ’--verbose’ , ’verbose mode’ , ’Display additional information during processin
6 array( ’--quick’ , ’"quick" operation’ , ’Do not check integrity, just make changes and f
7 array( ’-v’ , ’Same as --verbose’ , ’Display additional information during processin
8 array( ’-w’ , ’wide output’ , ’When listing users, use the whole available scr
9 );
10 }
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.
304 Chapter 70. Console-aware modules
Zend Framework 2 Documentation, Release 2.3.1dev
70.2.4 Mixing styles
You can use mix together all of the above styles to provide comprehensive usage information, for example:
1 public function getConsoleUsage(Console $console)
2 {
3 return array(
4 ’Finding and listing users’,
5 ’list [all|disabled] users [-w]’ => ’Show a list of users’,
6 ’find user [--email=] [--name=]’ => ’Attempt to find a user by email or name’,
7
8 array(’[all|disabled]’, ’Display all users or only disabled accounts’),
9 array(’--email=EMAIL’, ’Email of the user to find’),
10 array(’--name=NAME’, ’Full name of the user to find.’),
11 array(’-w’, ’Wide output - When listing users use the whole available screen w
12
13 ’Manipulation of user database:’,
14 ’delete user <userEmail> [--verbose|-v] [--quick]’ => ’Delete user with email <userEmail>’,
15 ’disable user <userEmail> [--verbose|-v]’ => ’Disable user with email <userEmail>’,
16
17 array( ’<userEmail>’ , ’user email’ , ’Full email address of the user to change.’ ),
18 array( ’--verbose’ , ’verbose mode’ , ’Display additional information during processin
19 array( ’--quick’ , ’"quick" operation’ , ’Do not check integrity, just make changes and f
20 array( ’-v’ , ’Same as --verbose’ , ’Display additional information during processin
21
22 );
23 }
70.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.
70.3. Best practices 305
Zend Framework 2 Documentation, Release 2.3.1dev
306 Chapter 70. Console-aware modules
CHAPTER 71
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
71.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 Service-
Manager, 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 // FILE: modules/Application/config/module.config.php
2 array(
3 ’router’ => array(
4 ’routes’ => array(
5 // HTTP routes are here
6 )
7 ),
8
9 ’console’ => array(
10 ’router’ => array(
11 ’routes’ => array(
12 ’list-users’ => array(
13 ’options’ => array(
14 ’route’ => ’show [all|disabled|deleted]:mode users [--verbose|-v]’,
15 ’defaults’ => array(
16 ’controller’ => ’ApplicationControllerIndex’,
17 ’action’ => ’show-users’
307
Zend Framework 2 Documentation, Release 2.3.1dev
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 <?php
2 namespace ApplicationController;
3
4 use ZendMvcControllerAbstractActionController;
5 use ZendViewModelViewModel;
6
7 class IndexController extends AbstractActionController
8 {
9 public function indexAction()
10 {
11 return new ViewModel(); // display standard index page
12 }
13
14 public function showUsersAction()
15 {
16 $request = $this->getRequest();
17
18 // Check verbose flag
19 $verbose = $request->getParam(’verbose’) || $request->getParam(’v’);
20
21 // Check mode
22 $mode = $request->getParam(’mode’, ’all’); // defaults to ’all’
23
24 $users = array();
25 switch ($mode) {
26 case ’disabled’:
27 $users = $this->getServiceLocator()->get(’users’)->fetchDisabledUsers();
28 break;
29 case ’deleted’:
30 $users = $this->getServiceLocator()->get(’users’)->fetchDeletedUsers();
31 break;
32 case ’all’:
33 default:
34 $users = $this->getServiceLocator()->get(’users’)->fetchAllUsers();
35 break;
36 }
37 }
38 }
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.
308 Chapter 71. Console-aware action controllers
Zend Framework 2 Documentation, Release 2.3.1dev
71.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 public function showUsersAction()
2 {
3 $request = $this->getRequest();
4
5 // Check verbose flag
6 $verbose = $request->getParam(’verbose’) || $request->getParam(’v’);
7
8 // Check mode
9 $mode = $request->getParam(’mode’, ’all’); // defaults to ’all’
10
11 $users = array();
12 switch ($mode) {
13 case ’disabled’:
14 $users = $this->getServiceLocator()->get(’users’)->fetchDisabledUsers();
15 break;
16 case ’deleted’:
17 $users = $this->getServiceLocator()->get(’users’)->fetchDeletedUsers();
18 break;
19 case ’all’:
20 default:
21 $users = $this->getServiceLocator()->get(’users’)->fetchAllUsers();
22 break;
23 }
24
25 if (count($users) == 0) {
26 // Show an error message in the console
27 return "There are no users in the databasen";
28 }
29
30 $result = ’’;
31
32 foreach ($users as $user) {
33 $result .= $user->name . ’ ’ . $user->email . "n";
34 }
35
36 return $result; // show it in the console
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.
71.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:
71.2. Sending output to console 309
Zend Framework 2 Documentation, Release 2.3.1dev
1 namespace ApplicationController;
2
3 use ZendMvcControllerAbstractActionController;
4 use ZendViewModelViewModel;
5 use ZendConsoleRequest as ConsoleRequest;
6 use RuntimeException;
7
8 class IndexController extends AbstractActionController
9 {
10 public function showUsersAction()
11 {
12 $request = $this->getRequest();
13
14 // Make sure that we are running in a console and the user has not tricked our
15 // application into running this action from a public web server.
16 if (!$request instanceof ConsoleRequest) {
17 throw new RuntimeException(’You can only use this action from a console!’);
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 use ZendMvcControllerAbstractActionController;
4 use ZendViewModelViewModel;
5 use ZendConsoleRequest as ConsoleRequest;
6 use ZendHttpRequest as HttpRequest;
7 use RuntimeException;
8
9 class IndexController extends AbstractActionController
10 {
11 public function showUsersAction()
12 {
13 $request = $this->getRequest();
14
15 $users = array();
16 // ... fetch users from database ...
17
18 if ($request instanceof HttpRequest) {
19 // display a web page with users list
20 return new ViewModel($result);
21 } elseif ($request instanceof ConsoleRequest) {
22 // ... prepare console output and return it ...
23 return $result;
24 } else {
25 throw new RuntimeException(’Cannot handle request of type ’ . get_class($request));
26 }
27 }
310 Chapter 71. Console-aware action controllers
Zend Framework 2 Documentation, Release 2.3.1dev
28 }
71.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.
71.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 // inside of config.console.router.routes:
2 ’show-users’ => array(
3 ’options’ => array(
4 ’route’ => ’show (all|deleted|locked|admin) [<groupName>]’
5 ’defaults’ => array(
6 ’controller’ => ’ApplicationControllerUsers’,
7 ’action’ => ’showusers’
8 )
9 )
10 )
If this route matches, our action can now query parameters in the following way:
1 // an action inside ApplicationControllerUsersController:
2 public function showUsersAction()
3 {
4 $request = $this->getRequest();
5
6 // We can access named value parameters directly by their name:
7 $showUsersFromGroup = $request->getParam(’groupName’);
8
9 // Literal parameters can be checked with isset() against their exact spelling
10 if (isset($request->getParam(’all’))) {
11 // show all users
12 } elseif (isset($request->getParam(’deleted’))) {
13 // show deleted users
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:
1 // inside of config.console.router.routes:
2 ’show-users’ => array(
3 ’options’ => array(
4 ’route’ => ’show (all|deleted|locked|admin):userTypeFilter [<groupName>]’
5 ’defaults’ => array(
6 ’controller’ => ’ApplicationControllerUsers’,
7 ’action’ => ’showusers’
8 )
71.4. Reading values from console parameters 311
Zend Framework 2 Documentation, Release 2.3.1dev
9 )
10 )
Now we can use a the group name userTypeFilter to check which option has been selected by the user:
1 public function showUsersAction()
2 {
3 $request = $this->getRequest();
4
5 // We can access named value parameters directly by their name:
6 $showUsersFromGroup = $request->getParam(’groupName’);
7
8 // The selected option from second parameter is now stored under ’userTypeFilter’
9 $userTypeFilter = $request->getParam(’userTypeFilter’);
10
11 switch ($userTypeFilter) {
12 case ’all’:
13 // all users
14 case ’deleted’:
15 // deleted users
16 case ’locked’
17 // ...
18 // ...
19 }
20 }
71.4.2 Flags
Flags are directly accessible by name. Value-capturing flags will contain string values, as provided by the user. Non-
value flags will be equal to true.
Given the following route:
1 ’find-user’ => array(
2 ’options’ => array(
3 ’route’ => ’find user [--fast] [--verbose] [--id=] [--firstName=] [--lastName=] [--email=]
4 ’defaults’ => array(
5 ’controller’ => ’ApplicationControllerUsers’,
6 ’action’ => ’find’,
7 )
8 )
9 )
We can easily retrieve values in the following fashion:
1 public function findAction()
2 {
3 $request = $this->getRequest();
4
5 // We can retrieve values from value flags using their name
6 $searchId = $request->getParam(’id’, null); // default null
7 $searchFirstName = $request->getParam(’firstName’, null);
8 $searchLastName = $request->getParam(’lastName’, null);
9 $searchEmail = $request->getParam(’email’, null);
10
11 // Standard flags that have been matched will be equal to TRUE
12 $isFast = (bool) $request->getParam(’fast’, false); // default false
13 $isVerbose = (bool) $request->getParam(’verbose’,false);
312 Chapter 71. Console-aware action controllers
Zend Framework 2 Documentation, Release 2.3.1dev
14
15 if ($isFast) {
16 // perform a fast query ...
17 } else {
18 // perform standard query ...
19 }
20 }
In case of flag alternatives, we have to check each alternative separately:
1 // Assuming our route now reads:
2 // ’route’ => ’find user [--fast|-f] [--verbose|-v] ... ’,
3 //
4 public function findAction()
5 {
6 $request = $this->getRequest();
7
8 // Check both alternatives
9 $isFast = $request->getParam(’fast’,false) || $request->getParam(’f’,false);
10 $isVerbose = $request->getParam(’verbose’,false) || $request->getParam(’v’,false);
11
12 // ...
13 }
71.4. Reading values from console parameters 313
Zend Framework 2 Documentation, Release 2.3.1dev
314 Chapter 71. Console-aware action controllers
CHAPTER 72
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.
72.1 Retrieving console adapter
If you are using MVC controllers you can obtain Console adapter instance using Service Manager.
1 namespace Application;
2
3 use ZendMvcControllerAbstractActionController;
4 use ZendConsoleAdapterAdapterInterface as Console;
5 use ZendConsoleExceptionRuntimeException;
6
7 class ConsoleController extends AbstractActionController
8 {
9 public function testAction()
10 {
11 $console = $this->getServiceLocator()->get(’console’);
12 if (!$console instanceof Console) {
13 throw new RuntimeException(’Cannot obtain console adapter. Are we running in a console?’)
14 }
15 }
16 }
If you are using ZendConsole without MVC, we can get adapter using the following code:
1 use ZendConsoleConsole;
2 use ZendConsoleExceptionRuntimeException as ConsoleException;
3
4 try {
5 $console = Console::getInstance();
6 } catch (ConsoleException $e) {
315
Zend Framework 2 Documentation, Release 2.3.1dev
7 // Could not get console adapter - most likely we are not running inside a console window.
8 }
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.
72.2 Using console adapter
72.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 char-
acters).
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.
72.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.
72.2.3 Writing to console
$console->write( string $text, $color = null, $bgColor = null ) Write a $text to the console, optionally us-
ing 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.
316 Chapter 72. Console adapters
Zend Framework 2 Documentation, Release 2.3.1dev
72.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.
72.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.
72.2. Using console adapter 317
Zend Framework 2 Documentation, Release 2.3.1dev
318 Chapter 72. Console adapters
CHAPTER 73
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 $confirm = new PromptConfirm(’Are you sure you want to continue?’);
4 $result = $confirm->show();
5 if ($result) {
6 // the user chose to continue
7 }
There is also a shorter method of displaying prompts, using static prompt() method:
1 use ZendConsolePrompt;
2
3 $result = PromptConfirm::prompt(’Are you sure you want to continue?’);
4 if ($result) {
5 // the user chose to continue
6 }
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.
73.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.
319
Zend Framework 2 Documentation, Release 2.3.1dev
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");
}
73.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
);
$console->write("Good day to you $name!");
73.3 Char
This prompt reads a single keystroke and optionally validates it against a list o allowed characters.
Char(
string $text = ’Please hit a key’,
string $allowedChars = ’abc’,
bool $ignoreCase = true,
bool $allowEmpty = false,
bool $echo = true
)
$text (string) The text to show with the prompt
$allowedChars (string) A list of allowed keys that can be pressed.
320 Chapter 73. Console prompts
Zend Framework 2 Documentation, Release 2.3.1dev
$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.’);
}
73.4 Select
This prompt displays a number of choices and asks the user to pick one.
Select(
string $text = ’Please select one option’,
array $options = array(),
bool $allowEmpty = false,
bool $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 = array(
’a’ => ’Apples’,
’o’ => ’Oranges’,
’p’ => ’Pears’,
’b’ => ’Bananas’,
’n’ => ’none of the above...’
);
$answer = Select::prompt(
’Which fruit do you like the best?’,
$options,
false,
false
73.4. Select 321
Zend Framework 2 Documentation, Release 2.3.1dev
);
$console->write("You told me that you like " . $options[$answer]);
See also:
To learn more about accessing console, writing to and reading from it, make sure to read the following chapter:
Console adapters.
322 Chapter 73. Console prompts
CHAPTER 74
Introduction
The ZendConsoleGetopt class helps command-line applications to parse their options and arguments.
Users may specify command-line arguments when they execute your application. These arguments have meaning to
the application, to change the behavior in some way, or choose resources, or specify parameters. Many options have
developed customary meaning, for example --verbose enables extra output from many applications. Other options
may have a meaning that is different for each application. For example, -c enables different features in grep, ls,
and tar.
Below are a few definitions of terms. Common usage of the terms varies, but this documentation will use the definitions
below.
• “argument”: a string that occurs on the command-line following the name of the command. Arguments may be
options or else may appear without an option, to name resources on which the command operates.
• “option”: an argument that signifies that the command should change its default behavior in some way.
• “flag”: the first part of an option, identifies the purpose of the option. A flag is preceded conventionally by one
or two dashes (- or --). A single dash precedes a single-character flag or a cluster of single-character flags. A
double-dash precedes a multi-character flag. Long flags cannot be clustered.
• “parameter”: the secondary part of an option; a data value that may accompany a flag, if it is applicable to
the given option. For example, many commands accept a --verbose option, but typically this option has no
parameter. However, an option like --user almost always requires a following parameter.
A parameter may be given as a separate argument following a flag argument, or as part of the same argu-
ment string, separated from the flag by an equals symbol (=). The latter form is supported only by long
flags. For example, -u username, --user username, and --user=username are forms supported
by ZendConsoleGetopt.
• “cluster”: multiple single-character flags combined in a single string argument and preceded by a single dash.
For example, “ls -1str” uses a cluster of four short flags. This command is equivalent to “ls -1 -s -t
-r”. Only single-character flags can be clustered. You cannot make a cluster of long flags.
For example, in mysql --user=root mydatabase, mysql is a command, --user=root is an option,
--user is a flag, root is a parameter to the option, and mydatabase is an argument but not an option by our
definition.
ZendConsoleGetopt provides an interface to declare which flags are valid for your application, output an error
and usage message if they use an invalid flag, and report to your application code which flags the user specified.
Note: Getopt is not an Application Framework
ZendConsoleGetopt does not interpret the meaning of flags and parameters, nor does this class implement
application workflow or invoke application code. You must implement those actions in your own application code.
You can use the ZendConsoleGetopt class to parse the command-line and provide object-oriented methods
323
Zend Framework 2 Documentation, Release 2.3.1dev
for querying which options were given by a user, but code to use this information to invoke parts of your application
should be in another PHP class.
The following sections describe usage of ZendConsoleGetopt.
324 Chapter 74. Introduction
CHAPTER 75
Declaring Getopt Rules
The constructor for the ZendConsoleGetopt class takes from one to three arguments. The first argument
declares which options are supported by your application. This class supports alternative syntax forms for declaring
the options. See the sections below for the format and usage of these syntax forms.
The constructor takes two more arguments, both of which are optional. The second argument may contain the
command-line arguments. This defaults to $_SERVER[’argv’].
The third argument of the constructor may contain an configuration options to customize the behavior of
ZendConsoleGetopt. See Adding Configuration for reference on the options available.
75.1 Declaring Options with the Short Syntax
ZendConsoleGetopt supports a compact syntax similar to that used by GNU Getopt (see
http://www.gnu.org/software/libc/manual/html_node/Getopt.html. This syntax supports only single-character
flags. In a single string, you type each of the letters that correspond to flags supported by your application. A letter
followed by a colon character (:) indicates a flag that requires a parameter.
Using the Short Syntax
1 $opts = new ZendConsoleGetopt(’abp:’);
The example above shows using ZendConsoleGetopt to declare that options may be given as -a, -b, or -p.
The latter flag requires a parameter.
The short syntax is limited to flags of a single character. Aliases, parameter types, and help strings are not supported
in the short syntax.
75.2 Declaring Options with the Long Syntax
A different syntax with more features is also available. This syntax allows you to specify aliases for flags, types of
option parameters, and also help strings to describe usage to the user. Instead of the single string used in the short
syntax to declare the options, the long syntax uses an associative array as the first argument to the constructor.
The key of each element of the associative array is a string with a format that names the flag, with any aliases, separated
by the pipe symbol (“|”). Following this series of flag aliases, if the option requires a parameter, is an equals symbol
(“=”) with a letter that stands for the type of the parameter:
• “=s” for a string parameter
325
Zend Framework 2 Documentation, Release 2.3.1dev
• “=w” for a word parameter (a string containing no whitespace)
• “=i” for an integer parameter
If the parameter is optional, use a dash (“-”) instead of the equals symbol.
The value of each element in the associative array is a help string to describe to a user how to use your program.
Using the Long Syntax
1 $opts = new ZendConsoleGetopt(
2 array(
3 ’apple|a’ => ’apple option, with no parameter’,
4 ’banana|b=i’ => ’banana option, with required integer parameter’,
5 ’pear|p-s’ => ’pear option, with optional string parameter’
6 )
7 );
In the example declaration above, there are three options. --apple and -a are aliases for each other, and the option
takes no parameter. --banana and -b are aliases for each other, and the option takes a mandatory integer parameter.
Finally, --pear and -p are aliases for each other, and the option may take an optional string parameter.
326 Chapter 75. Declaring Getopt Rules
CHAPTER 76
Fetching Options and Arguments
After you have declared the options that the ZendConsoleGetopt object should recognize, and supply argu-
ments from the command-line or an array, you can query the object to find out which options were specified by a
user in a given command-line invocation of your program. The class implements magic methods so you can query for
options by name.
The parsing of the data is deferred until the first query you make against the ZendConsoleGetopt object to find
out if an option was given, the object performs its parsing. This allows you to use several method calls to configure
the options, arguments, help strings, and configuration options before parsing takes place.
76.1 Handling Getopt Exceptions
If the user gave any invalid options on the command-line, the parsing function throws a
ZendConsoleGetoptException. You should catch this exception in your application code. You
can use the parse() method to force the object to parse the arguments. This is useful because you can invoke
parse() in a try block. If it passes, you can be sure that the parsing won’t throw an exception again. The exception
thrown has a custom method getUsageMessage(), which returns as a string the formatted set of usage messages
for all declared options.
Catching Getopt Exceptions
1 try {
2 $opts = new ZendConsoleGetopt(’abp:’);
3 $opts->parse();
4 } catch (ZendConsoleGetoptException $e) {
5 echo $e->getUsageMessage();
6 exit;
7 }
Cases where parsing throws an exception include:
• Option given is not recognized.
• Option requires a parameter but none was given.
• Option parameter is of the wrong type. E.g. a non-numeric string when an integer was required.
327
Zend Framework 2 Documentation, Release 2.3.1dev
76.2 Fetching Options by Name
You can use the getOption() method to query the value of an option. If the option had a parameter, this method
returns the value of the parameter. If the option had no parameter but the user did specify it on the command-line, the
method returns TRUE. Otherwise the method returns NULL.
Using getOption()
1 $opts = new ZendConsoleGetopt(’abp:’);
2 $b = $opts->getOption(’b’);
3 $p_parameter = $opts->getOption(’p’);
Alternatively, you can use the magic __get() function to retrieve the value of an option as if it were a class member
variable. The __isset() magic method is also implemented.
Using __get() and __isset() Magic Methods
1 $opts = new ZendConsoleGetopt(’abp:’);
2 if (isset($opts->b)) {
3 echo "I got the b option.n";
4 }
5 $p_parameter = $opts->p; // null if not set
If your options are declared with aliases, you may use any of the aliases for an option in the methods above.
76.3 Reporting Options
There are several methods to report the full set of options given by the user on the current command-line.
• As a string: use the toString() method. The options are returned as a space-separated string of
flag=value pairs. The value of an option that does not have a parameter is the literal string “TRUE”.
• As an array: use the toArray() method. The options are returned in a simple integer-indexed array of strings,
the flag strings followed by parameter strings, if any.
• As a string containing JSON data: use the toJson() method.
• As a string containing XML data: use the toXml() method.
In all of the above dumping methods, the flag string is the first string in the corresponding list of aliases. For example,
if the option aliases were declared like verbose|v, then the first string, verbose, is used as the canonical name of
the option. The name of the option flag does not include any preceding dashes.
76.4 Fetching Non-option Arguments
After option arguments and their parameters have been parsed from the command-line, there may be additional argu-
ments remaining. You can query these arguments using the getRemainingArgs() method. This method returns
an array of the strings that were not part of any options.
328 Chapter 76. Fetching Options and Arguments
Zend Framework 2 Documentation, Release 2.3.1dev
Using getRemainingArgs()
1 $opts = new ZendConsoleGetopt(’abp:’);
2 $opts->setArguments(array(’-p’, ’p_parameter’, ’filename’));
3 $args = $opts->getRemainingArgs(); // returns array(’filename’)
ZendConsoleGetopt supports the GNU convention that an argument consisting of a double-dash signifies the
end of options. Any arguments following this signifier must be treated as non-option arguments. This is useful if you
might have a non-option argument that begins with a dash. For example: “rm -- -filename-with-dash”.
76.4. Fetching Non-option Arguments 329
Zend Framework 2 Documentation, Release 2.3.1dev
330 Chapter 76. Fetching Options and Arguments
CHAPTER 77
Configuring ZendConsoleGetopt
77.1 Adding Option Rules
You can add more option rules in addition to those you specified in the ZendConsoleGetopt constructor, using
the addRules() method. The argument to addRules() is the same as the first argument to the class constructor.
It is either a string in the format of the short syntax options specification, or else an associative array in the format of
a long syntax options specification. See Declaring Getopt Rules for details on the syntax for specifying options.
Using addRules()
1 $opts = new ZendConsoleGetopt(’abp:’);
2 $opts->addRules(
3 array(
4 ’verbose|v’ => ’Print verbose output’
5 )
6 );
The example above shows adding the --verbose option with an alias of -v to a set of options defined in the call
to the constructor. Notice that you can mix short format options and long format options in the same instance of
ZendConsoleGetopt.
77.2 Adding Help Messages
In addition to specifying the help strings when declaring option rules in the long format, you can associate help strings
with option rules using the setHelp() method. The argument to the setHelp() method is an associative array,
in which the key is a flag, and the value is a corresponding help string.
Using setHelp()
1 $opts = new ZendConsoleGetopt(’abp:’);
2 $opts->setHelp(
3 array(
4 ’a’ => ’apple option, with no parameter’,
5 ’b’ => ’banana option, with required integer parameter’,
6 ’p’ => ’pear option, with optional string parameter’
7 )
8 );
331
Zend Framework 2 Documentation, Release 2.3.1dev
If you declared options with aliases, you can use any of the aliases as the key of the associative array.
The setHelp() method is the only way to define help strings if you declared the options using the short syntax.
77.3 Adding Option Aliases
You can declare aliases for options using the setAliases() method. The argument is an associative array, whose
key is a flag string declared previously, and whose value is a new alias for that flag. These aliases are merged with any
existing aliases. In other words, aliases you declared earlier are still in effect.
An alias may be declared only once. If you try to redefine an alias, a ZendConsoleGetoptException is
thrown.
Using setAliases()
1 $opts = new ZendConsoleGetopt(’abp:’);
2 $opts->setAliases(
3 array(
4 ’a’ => ’apple’,
5 ’a’ => ’apfel’,
6 ’p’ => ’pear’
7 )
8 );
In the example above, after declaring these aliases, -a, --apple and --apfel are aliases for each other. Also -p
and --pear are aliases for each other.
The setAliases() method is the only way to define aliases if you declared the options using the short syntax.
77.4 Adding Argument Lists
By default, ZendConsoleGetopt uses $_SERVER[’argv’] for the array of command-line arguments to
parse. You can alternatively specify the array of arguments as the second constructor argument. Finally, you can
append more arguments to those already used using the addArguments() method, or you can replace the current
array of arguments using the setArguments() method. In both cases, the parameter to these methods is a simple
array of strings. The former method appends the array to the current arguments, and the latter method substitutes the
array for the current arguments.
Using addArguments() and setArguments()
1 // By default, the constructor uses $_SERVER[’argv’]
2 $opts = new ZendConsoleGetopt(’abp:’);
3
4 // Append an array to the existing arguments
5 $opts->addArguments(array(’-a’, ’-p’, ’p_parameter’, ’non_option_arg’));
6
7 // Substitute a new array for the existing arguments
8 $opts->setArguments(array(’-a’, ’-p’, ’p_parameter’, ’non_option_arg’));
332 Chapter 77. Configuring ZendConsoleGetopt
Zend Framework 2 Documentation, Release 2.3.1dev
77.5 Adding Configuration
The third parameter to the ZendConsoleGetopt constructor is an array of configuration options that affect
the behavior of the object instance returned. You can also specify configuration options using the setOptions()
method, or you can set an individual option using the setOption() method.
Note: Clarifying the Term “option”
The term “option” is used for configuration of the ZendConsoleGetopt class to match terminology used
elsewhere in Zend Framework. These are not the same things as the command-line options that are parsed by the
ZendConsoleGetopt class.
The currently supported options have const definitions in the class. The options, their const identifiers (with literal
values in parentheses) are listed below:
• ZendConsoleGetopt::CONFIG_DASHDASH (“dashDash”), if TRUE, enables the special flag -- to
signify the end of flags. Command-line arguments following the double-dash signifier are not interpreted as
options, even if the arguments start with a dash. This configuration option is TRUE by default.
• ZendConsoleGetopt::CONFIG_IGNORECASE (“ignoreCase”), if TRUE, makes flags aliases of each
other if they differ only in their case. That is, -a and -A will be considered to be synonymous flags. This
configuration option is FALSE by default.
• ZendConsoleGetopt::CONFIG_RULEMODE (“ruleMode”) may have values
ZendConsoleGetopt::MODE_ZEND (“zend”) and ZendConsoleGetopt::MODE_GNU (“gnu”).
It should not be necessary to use this option unless you extend the class with additional syntax forms. The two
modes supported in the base ZendConsoleGetopt class are unambiguous. If the specifier is a string, the
class assumes MODE_GNU, otherwise it assumes MODE_ZEND. But if you extend the class and add more syntax
forms, you may need to specify the mode using this option.
More configuration options may be added as future enhancements of this class.
The two arguments to the setOption() method are a configuration option name and an option value.
Using setOption()
1 $opts = new ZendConsoleGetopt(’abp:’);
2 $opts->setOption(’ignoreCase’, true);
The argument to the setOptions() method is an associative array. The keys of this array are the configuration
option names, and the values are configuration values. This is also the array format used in the class constructor. The
configuration values you specify are merged with the current configuration; you don’t have to list all options.
Using setOptions()
1 $opts = new ZendConsoleGetopt(’abp:’);
2 $opts->setOptions(
3 array(
4 ’ignoreCase’ => true,
5 ’dashDash’ => false
6 )
7 );
77.5. Adding Configuration 333
Zend Framework 2 Documentation, Release 2.3.1dev
334 Chapter 77. Configuring ZendConsoleGetopt
CHAPTER 78
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.
335
Zend Framework 2 Documentation, Release 2.3.1dev
336 Chapter 78. Introduction to ZendCrypt
CHAPTER 79
Encrypt/decrypt using block ciphers
ZendCryptBlockCipher implements the encrypt-then-authenticate mode using HMAC to provide authentica-
tion.
The symmetric cipher can be chosen 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 $blockCipher = BlockCipher::factory(’mcrypt’, array(’algo’ => ’aes’));
4 $blockCipher->setKey(’encryption key’);
5 $result = $blockCipher->encrypt(’this is a secret message’);
6 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
3 $blockCipher = BlockCipher::factory(’mcrypt’, array(
4 ’algo’ => ’blowfish’,
5 ’mode’ => ’cfb’,
337
Zend Framework 2 Documentation, Release 2.3.1dev
6 ’hash’ => ’sha512’
7 ));
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 use ZendCryptBlockCipher;
2 use ZendCryptSymmetricMcrypt;
3
4 $blockCipher = new BlockCipher(new Mcrypt(array(’algo’ => ’aes’)));
5 $blockCipher->setKey(’encryption key’);
6 $result = $blockCipher->encrypt(’this is a secret message’);
7 echo "Encrypted text: $result n";
338 Chapter 79. Encrypt/decrypt using block ciphers
CHAPTER 80
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.
80.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 use ZendCryptKeyDerivationPbkdf2;
2 use ZendMathRand;
3
4 $pass = ’password’;
5 $salt = Rand::getBytes(32, true);
6 $key = Pbkdf2::calc(’sha256’, $pass, $salt, 10000, 32);
7
8 printf ("Original password: %sn", $pass);
9 printf ("Derived key (hex): %sn", bin2hex($key));
The Pbkdf2 adapter takes the password ($pass) and generate a binary key of 32 bytes. The syntax is
calc($hash, $pass, $salt, $iterations, $length) where $hash is the name of the hash func-
tion 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
the ZendMathRand class to generate a random string of 32 bytes for the salt, using a strong generator (the true
value means the usage of a cryptographically strong generator).
The number of iterations is a very important parameter for the security of the algorithm. Bigger values guarantee more
security. There is not a fixed value for that because the number of iterations depends on the CPU power. You should
339
Zend Framework 2 Documentation, Release 2.3.1dev
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.
80.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 to generate a key of 32 bytes.
1 use ZendCryptKeyDerivationSaltedS2k;
2 use ZendMathRand;
3
4 $pass = ’password’;
5 $salt = Rand::getBytes(32, true);
6 $key = SaltedS2k::calc(’sha256’, $pass, $salt, 32);
7
8 printf ("Original password: %sn", $pass);
9 printf ("Derived key (hex): %sn", bin2hex($key));
80.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 attacker’s 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 32 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.
1 use ZendCryptKeyDerivationScrypt;
2 use ZendMathRand;
3
4 $pass = ’password’;
340 Chapter 80. Key derivation function
Zend Framework 2 Documentation, Release 2.3.1dev
5 $salt = Rand::getBytes(32, true);
6 $key = Scrypt::calc($pass, $salt, 2048, 2, 1, 32);
7
8 printf ("Original password: %sn", $pass);
9 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.
80.3. Scrypt adapter 341
Zend Framework 2 Documentation, Release 2.3.1dev
342 Chapter 80. Key derivation function
CHAPTER 81
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).
81.1 Bcrypt
The bcrypt algorithm is an hashing algorithm that is widely used and suggested by the security community to store
user’s 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 10, that means
about 0.07 second using a CPU Intel i5 at 3.3Ghz (the cost parameter is a relative value according to the speed of the
CPU used). We changed the default value of the cost parameter from 14 to 10, starting from Zend Framework 2.3.0,
due to high computational time to prevent potential denial-of-service attacks (you can read this article Aggressive
password stretching for more information).
If you want to change the cost parameter of the bcrypt algorithm you can use the setCost() method. Please note,
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
3 $bcrypt = new Bcrypt();
4 $securePass = $bcrypt->create(’user password’);
343
Zend Framework 2 Documentation, Release 2.3.1dev
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 $bcrypt = new Bcrypt();
4 $securePass = ’the stored bcrypt value’;
5 $password = ’the password to check’;
6
7 if ($bcrypt->verify($password, $securePass)) {
8 echo "The password is correct! n";
9 } else {
10 echo "The password is NOT correct.n";
11 }
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 $bcrypt = new Bcrypt(array(
4 ’salt’ => ’random value’,
5 ’cost’ => 11
6 ));
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 char-
acters 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.
81.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;
• 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.
344 Chapter 81. Password
Zend Framework 2 Documentation, Release 2.3.1dev
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 $apache->setFormat(’crypt’);
6 printf ("CRYPT output: %sn", $apache->create(’password’));
7
8 $apache->setFormat(’sha1’);
9 printf ("SHA1 output: %sn", $apache->create(’password’));
10
11 $apache->setFormat(’md5’);
12 printf ("MD5 output: %sn", $apache->create(’password’));
13
14 $apache->setFormat(’digest’);
15 $apache->setUserName(’enrico’);
16 $apache->setAuthName(’test’);
17 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 $apache = new Apache(array(
4 ’format’ => ’md5’
5 ));
Other possible parameters to pass in the constructor are username and authname, for the digest format.
81.2. Apache 345
Zend Framework 2 Documentation, Release 2.3.1dev
346 Chapter 81. Password
CHAPTER 82
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.
82.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 Diffie–Hellman 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 Diffie-
Hellman 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 $aliceOptions = array(
4 ’prime’ => ’1551728981814736974712322577637155399157248019669154044797077953140576293785419175
5 ’4236981889937278161526466314385615958256881888899512721588426754199503412587065565
6 ’1048705376814767265132557470407658574792912915723345106432450947150072296210941943
7 ’984760375594985848253359305585439638443’,
8 ’generator’=> ’2’,
9 ’private’ => ’9920931406657259523640856959196798855714124956149426748625180803553539633227862014
10 ’81312712891672623072630995180324388841681491857745515696789091127409515009250358965
11 ’46342049838178521379132153348139908016819196219448310107072632515749339055798122538
12 ’04828702523796951800575031871051678091’
13 );
14
15 $bobOptions = array(
16 ’prime’ => $aliceOptions[’prime’],
17 ’generator’=> ’2’,
18 ’private’ => ’3341173579263955862573363571789256361254818065040216115107747831484146370794889978
347
Zend Framework 2 Documentation, Release 2.3.1dev
19 ’1232563473041055194677275288017786897281696355182174038670007603421340815392469256
20 ’6346473315660054548451083307242700347420706465071483108330449773716038209708335687
21 ’31616972608703322302585471319261275664’
22 );
23
24 $alice = new DiffieHellman($aliceOptions[’prime’], $aliceOptions[’generator’], $aliceOptions[’private
25 $bob = new DiffieHellman($bobOptions[’prime’], $bobOptions[’generator’], $bobOptions[’private’]);
26
27 $alice->generateKeys();
28 $bob->generateKeys();
29
30 $aliceSecretKey = $alice->computeSecretKey($bob->getPublicKey(DiffieHellman::FORMAT_BINARY),
31 DiffieHellman::FORMAT_BINARY,
32 DiffieHellman::FORMAT_BINARY);
33
34 $bobSecretKey = $bob->computeSecretKey($alice->getPublicKey(DiffieHellman::FORMAT_BINARY),
35 DiffieHellman::FORMAT_BINARY,
36 DiffieHellman::FORMAT_BINARY);
37
38 if ($aliceSecretKey !== $bobSecretKey) {
39 echo "ERROR!n";
40 } else {
41 printf("The secret key is: %sn", base64_encode($aliceSecretKey));
42 }
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.
82.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;
348 Chapter 82. Public key cryptography
Zend Framework 2 Documentation, Release 2.3.1dev
• encrypt/decrypt a string;
• generate a digital signature of a file.
82.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 $rsaOptions = new RsaOptions(array(
4 ’pass_phrase’ => ’test’
5 ));
6
7 $rsaOptions->generateKeys(array(
8 ’private_key_bits’ => 2048,
9 ));
10
11 file_put_contents(’private_key.pem’, $rsaOptions->getPrivateKey());
12 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
82.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 $rsa = Rsa::factory(array(
4 ’public_key’ => ’public_key.pub’,
5 ’private_key’ => ’private_key.pem’,
6 ’pass_phrase’ => ’test’,
7 ’binary_output’ => false
8 ));
9
10 $text = ’This is the message to encrypt’;
11
12 $encrypt = $rsa->encrypt($text);
13 printf("Encrypted message:n%sn", $encrypt);
14
15 $decrypt = $rsa->decrypt($encrypt);
16
17 if ($text !== $decrypt) {
18 echo "ERRORn";
19 } else {
82.2. RSA 349
Zend Framework 2 Documentation, Release 2.3.1dev
20 echo "Encryption and decryption performed successfully!n";
21 }
82.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 $rsa = Rsa::factory(array(
4 ’private_key’ => ’path/to/private_key’,
5 ’pass_phrase’ => ’passphrase of the private key’,
6 ’binary_output’ => false
7 ));
8
9 $file = file_get_contents(’path/file/to/sign’);
10
11 $signature = $rsa->sign($file, $rsa->getOptions()->getPrivateKey());
12 $verify = $rsa->verify($file, $signature, $rsa->getOptions()->getPublicKey());
13
14 if ($verify) {
15 echo "The signature is OKn";
16 file_put_contents($filename . ’.sig’, $signature);
17 echo "Signature save in $filename.sign";
18 } else {
19 echo "The signature is not valid!n";
20 }
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.
350 Chapter 82. Public key cryptography
CHAPTER 83
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.
83.1 Creating an Adapter - Quickstart
Creating an adapter can simply be done by instantiating the ZendDbAdapterAdapter class. The most com-
mon 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 Is Required? Value
driver required Mysqli, Sqlsrv, Pdo_Sqlite, Pdo_Mysql, Pdo=OtherPdoDriver
database generally required the name of the database (schema)
username generally required the connection username
password generally required the connection password
hostname not generally required the IP address or hostname to connect to
port not generally required the port to connect to (if applicable)
charset not generally required 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 $adapter = new ZendDbAdapterAdapter(array(
2 ’driver’ => ’Mysqli’,
3 ’database’ => ’zend_db_example’,
4 ’username’ => ’developer’,
5 ’password’ => ’developer-password’
6 ));
Another example, of a Sqlite connection via PDO:
351
Zend Framework 2 Documentation, Release 2.3.1dev
1 $adapter = new ZendDbAdapterAdapter(array(
2 ’driver’ => ’Pdo_Sqlite’,
3 ’database’ => ’path/to/sqlite.db’
4 ));
It is important to know that by using this style of adapter creation, the Adapter will attempt to create any depen-
dencies 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
83.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 use ZendDbAdapterPlatformPlatformInterface;
2 use ZendDbResultSetResultSet;
3
4 class ZendDbAdapterAdapter {
5 public function __construct($driver, PlatformInterface $platform = null, ResultSet $queryResultSe
6 }
What can be injected:
• $driver - an array of connection parameters (see above) or an instance of
ZendDbAdapterDriverDriverInterface
• $platform - (optional) an instance of ZendDbPlatformPlatformInterface, the default will be cre-
ated 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
83.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:
1 $adapter->query(’SELECT * FROM ‘artist‘ WHERE ‘id‘ = ?’, array(5));
The above example will go through the following steps:
352 Chapter 83. ZendDbAdapter
Zend Framework 2 Documentation, Release 2.3.1dev
• 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
83.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.
83.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 // with optional parameters to bind up-front
2 $statement = $adapter->createStatement($sql, $optionalParameters);
3 $result = $statement->execute();
83.6 Using the Driver Object
The Driver object is the primary place where ZendDbAdapterAdapter implements the connection level ab-
straction 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
• The adapter chooses the proper driver to instantiate, for example ZendDbAdapterDriverMysqli
• That driver class is instantiated
83.4. Query Execution Through ZendDbAdapterAdapter::query() 353
Zend Framework 2 Documentation, Release 2.3.1dev
• 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
3 interface DriverInterface
4 {
5 const PARAMETERIZATION_POSITIONAL = ’positional’;
6 const PARAMETERIZATION_NAMED = ’named’;
7 const NAME_FORMAT_CAMELCASE = ’camelCase’;
8 const NAME_FORMAT_NATURAL = ’natural’;
9 public function getDatabasePlatformName($nameFormat = self::NAME_FORMAT_CAMELCASE);
10 public function checkEnvironment();
11 public function getConnection();
12 public function createStatement($sqlOrResource = null);
13 public function createResult($resource);
14 public function getPrepareType();
15 public function formatParameterName($name, $type = null);
16 public function getLastGeneratedValue();
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 interface StatementInterface extends StatementContainerInterface
4 {
5 public function getResource();
6 public function prepare($sql = null);
7 public function isPrepared();
8 public function execute($parameters = null);
9
10 /** Inherited from StatementContainerInterface */
11 public function setSql($sql);
12 public function getSql();
13 public function setParameterContainer(ParameterContainer $parameterContainer);
14 public function getParameterContainer();
15 }
Result objects generally look like this:
354 Chapter 83. ZendDbAdapter
Zend Framework 2 Documentation, Release 2.3.1dev
1 namespace ZendDbAdapterDriver;
2
3 interface ResultInterface extends Countable, Iterator
4 {
5 public function buffer();
6 public function isQueryResult();
7 public function getAffectedRows();
8 public function getGeneratedValue();
9 public function getResource();
10 public function getFieldCount();
11 }
83.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 interface PlatformInterface
4 {
5 public function getName();
6 public function getQuoteIdentifierSymbol();
7 public function quoteIdentifier($identifier);
8 public function quoteIdentifierChain($identiferChain)
9 public function getQuoteValueSymbol();
10 public function quoteValue($value);
11 public function quoteValueList($valueList);
12 public function getIdentifierSeparator();
13 public function quoteIdentifierInFragment($identifier, array $additionalSafeWords = array());
14 }
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 $platform = $adapter->getPlatform();
2 // or
3 $platform = $adapter->platform; // magic property access
The following is a couple of example of Platform usage:
1 /** @var $adapter ZendDbAdapterAdapter */
2 /** @var $platform ZendDbAdapterPlatformSql92 */
3 $platform = $adapter->getPlatform();
4
5 // "first_name"
6 echo $platform->quoteIdentifier(’first_name’);
7
8 // "
9 echo $platform->getQuoteIdentifierSymbol();
10
11 // "schema"."mytable"
12 echo $platform->quoteIdentifierChain(array(’schema’,’mytable’)));
13
14 // ’
83.7. Using The Platform Object 355
Zend Framework 2 Documentation, Release 2.3.1dev
15 echo $platform->getQuoteValueSymbol();
16
17 // ’myvalue’
18 echo $platform->quoteValue(’myvalue’);
19
20 // ’value’, ’Foo O’Bar’
21 echo $platform->quoteValueList(array(’value’,"Foo O’Bar")));
22
23 // .
24 echo $platform->getIdentifierSeparator();
25
26 // "foo" as "bar"
27 echo $platform->quoteIdentifierInFragment(’foo as bar’);
28
29 // additionally, with some safe words:
30 // ("foo"."bar" = "boo"."baz")
31 echo $platform->quoteIdentifierInFragment(’(foo.bar = boo.baz)’, array(’(’, ’)’, ’=’));
83.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: */
public function count()
356 Chapter 83. ZendDbAdapter
Zend Framework 2 Documentation, Release 2.3.1dev
public function current()
public function next()
public function key()
public function valid()
public function 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 param-
eter:
$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.
83.9 Examples
Creating a Driver and Vendor portable Query, Preparing and Iterating Result
1 $adapter = new ZendDbAdapterAdapter($driverConfig);
2
3 $qi = function($name) use ($adapter) { return $adapter->platform->quoteIdentifier($name); };
4 $fp = function($name) use ($adapter) { return $adapter->driver->formatParameterName($name); };
5
6 $sql = ’UPDATE ’ . $qi(’artist’)
7 . ’ SET ’ . $qi(’name’) . ’ = ’ . $fp(’name’)
8 . ’ WHERE ’ . $qi(’id’) . ’ = ’ . $fp(’id’);
9
10 /** @var $statement ZendDbAdapterDriverStatementInterface */
11 $statement = $adapter->query($sql);
12
13 $parameters = array(
14 ’name’ => ’Updated Artist’,
15 ’id’ => 1
16 );
17
18 $statement->execute($parameters);
19
20 // DATA INSERTED, NOW CHECK
21
22 /* @var $statement ZendDbAdapterDriverStatementInterface */
23 $statement = $adapter->query(’SELECT * FROM ’
24 . $qi(’artist’)
25 . ’ WHERE id = ’ . $fp(’id’));
26
27 /* @var $results ZendDbResultSetResultSet */
28 $results = $statement->execute(array(’id’ => 1));
29
83.9. Examples 357
Zend Framework 2 Documentation, Release 2.3.1dev
30 $row = $results->current();
31 $name = $row[’name’];
358 Chapter 83. ZendDbAdapter
CHAPTER 84
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 sub-
components 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 interface ResultSetInterface extends Traversable, Countable
2 {
3 public function initialize($dataSource);
4 public function getFieldCount();
5 }
84.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 similar to what one might find inside
ZendDbAdapterAdapter::query():
1 use ZendDbAdapterDriverResultInterface;
2 use ZendDbResultSetResultSet;
3
4 $stmt = $driver->createStatement(’SELECT * FROM users’);
5 $stmt->prepare();
6 $result = $stmt->execute($parameters);
7
8 if ($result instanceof ResultInterface && $result->isQueryResult()) {
9 $resultSet = new ResultSet;
10 $resultSet->initialize($result);
11
12 foreach ($resultSet as $row) {
13 echo $row->my_column . PHP_EOL;
14 }
15 }
359
Zend Framework 2 Documentation, Release 2.3.1dev
84.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 abstract class AbstractResultSet implements Iterator, ResultSetInterface
2 {
3 public function initialize($dataSource)
4 public function getDataSource()
5 public function getFieldCount()
6
7 /** Iterator */
8 public function next()
9 public function key()
10 public function current()
11 public function valid()
12 public function rewind()
13
14 /** countable */
15 public function count()
16
17 /** get rows as array */
18 public function toArray()
19 }
84.3 ZendDbResultSetHydratingResultSet
ZendDbResultSetHydratingResultSet is a more flexible ResultSet object that allows the devel-
oper 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 use ZendDbAdapterDriverResultInterface;
2 use ZendDbResultSetHydratingResultSet;
3 use ZendStdlibHydratorReflection as ReflectionHydrator;
4
5 class UserEntity {
6 protected $first_name;
7 protected $last_name;
8 public function getFirstName() { return $this->first_name; }
9 public function getLastName() { return $this->last_name; }
10 public function setFirstName($first_name) { $this->first_name = $first_name; }
11 public function setLastName($last_name) { $this->last_name = $last_name; }
12 }
13
14 $stmt = $driver->createStatement($sql);
15 $stmt->prepare($parameters);
16 $result = $stmt->execute();
17
18 if ($result instanceof ResultInterface && $result->isQueryResult()) {
19 $resultSet = new HydratingResultSet(new ReflectionHydrator, new UserEntity);
20 $resultSet->initialize($result);
360 Chapter 84. ZendDbResultSet
Zend Framework 2 Documentation, Release 2.3.1dev
21
22 foreach ($resultSet as $user) {
23 echo $user->getFirstName() . ’ ’ . $user->getLastName() . PHP_EOL;
24 }
25 }
For more information, see the ZendStdlibHydrator documentation to get a better sense of the different strate-
gies that can be employed in order to populate a target object.
84.3. ZendDbResultSetHydratingResultSet 361
Zend Framework 2 Documentation, Release 2.3.1dev
362 Chapter 84. ZendDbResultSet
CHAPTER 85
ZendDbSql
ZendDbSql is a SQL abstraction layer for building platform specific SQL queries via an object-oriented API.
The end result of an ZendDbSql object will be to either produce a Statement and Parameter container that rep-
resents 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.
85.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 use ZendDbSqlSql;
2 $sql = new Sql($adapter);
3 $select = $sql->select(); // @return ZendDbSqlSelect
4 $insert = $sql->insert(); // @return ZendDbSqlInsert
5 $update = $sql->update(); // @return ZendDbSqlUpdate
6 $delete = $sql->delete(); // @return 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 use ZendDbSqlSql;
2 $sql = new Sql($adapter);
3 $select = $sql->select();
4 $select->from(’foo’);
5 $select->where(array(’id’ => 2));
6
7 $statement = $sql->prepareStatementForSqlObject($select);
8 $results = $statement->execute();
To execute (using a Select object)
1 use ZendDbSqlSql;
2 $sql = new Sql($adapter);
3 $select = $sql->select();
4 $select->from(’foo’);
363
Zend Framework 2 Documentation, Release 2.3.1dev
5 $select->where(array(’id’ => 2));
6
7 $selectString = $sql->getSqlStringForSqlObject($select);
8 $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 use ZendDbSqlSql;
2 $sql = new Sql($adapter, ’foo’);
3 $select = $sql->select();
4 $select->where(array(’id’ => 2)); // $select already has the from(’foo’) applied
85.2 ZendDbSql’s Select, Insert, Update and Delete
Each of these objects implements the following (2) interfaces:
1 interface PreparableSqlInterface {
2 public function prepareStatement(Adapter $adapter, StatementInterface $statement);
3 }
4 interface SqlInterface {
5 public function getSqlString(PlatformInterface $adapterPlatform = null);
6 }
These are the functions you can call to either produce (a) a prepared statement, or (b) a string to be executed.
85.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 use ZendDbSqlSelect;
2 $select = new Select();
3 // or, to produce a $select bound to a specific table
4 $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 class Select extends AbstractSql implements SqlInterface, PreparableSqlInterface
2 {
3 const JOIN_INNER = ’inner’;
4 const JOIN_OUTER = ’outer’;
5 const JOIN_LEFT = ’left’;
6 const JOIN_RIGHT = ’right’;
7 const SQL_STAR = ’*’;
8 const ORDER_ASCENDING = ’ASC’;
9 const ORDER_DESCENDING = ’DESC’;
10
11 public $where; // @param Where $where
12
13 public function __construct($table = null);
14 public function from($table);
15 public function columns(array $columns, $prefixColumnsWithTable = true);
364 Chapter 85. ZendDbSql
Zend Framework 2 Documentation, Release 2.3.1dev
16 public function join($name, $on, $columns = self::SQL_STAR, $type = self::JOIN_INNER);
17 public function where($predicate, $combination = PredicatePredicateSet::OP_AND);
18 public function group($group);
19 public function having($predicate, $combination = PredicatePredicateSet::OP_AND);
20 public function order($order);
21 public function limit($limit);
22 public function offset($offset);
23 }
85.3.1 from():
1 // as a string:
2 $select->from(’foo’);
3
4 // as an array to specify an alias:
5 // produces SELECT "t".* FROM "table" AS "t"
6
7 $select->from(array(’t’ => ’table’));
8
9 // using a SqlTableIdentifier:
10 // same output as above
11
12 $select->from(new TableIdentifier(array(’t’ => ’table’)));
85.3.2 columns():
1 // as array of names
2 $select->columns(array(’foo’, ’bar’));
3
4 // as an associative array with aliases as the keys:
5 // produces ’bar’ AS ’foo’, ’bax’ AS ’baz’
6
7 $select->columns(array(’foo’ => ’bar’, ’baz’ => ’bax’));
85.3.3 join():
1 $select->join(
2 ’foo’, // table name
3 ’id = bar.id’, // expression to join on (will be quoted by platform object before insertion),
4 array(’bar’, ’baz’), // (optional) list of columns, same requirements as columns() above
5 $select::JOIN_OUTER // (optional), one of inner, outer, left, right also represented by constant
6 );
7
8 $select->from(array(’f’ => ’foo’)) // base table
9 ->join(array(’b’ => ’bar’), // join table with alias
10 ’f.foo_id = b.foo_id’); // join expression
85.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:
85.3. ZendDbSqlSelect 365
Zend Framework 2 Documentation, Release 2.3.1dev
1 /**
2 * Create where clause
3 *
4 * @param Where|Closure|string|array $predicate
5 * @param string $combination One of the OP_* constants from PredicatePredicateSet
6 * @return Select
7 */
8 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 $spec = function (Where $where) {
2 $where->like(’username’, ’ralph%’);
3 };
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 // SELECT "foo".* FROM "foo" WHERE x = 5
2 $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 // SELECT "foo".* FROM "foo" WHERE x = 5 AND y = z
2 $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 // SELECT "foo".* FROM "foo" WHERE "c1" IS NULL AND "c2" IN (?, ?, ?) AND "c3" IS NOT NULL
2 $select->from(’foo’)->where(array(
3 ’c1’ => null,
4 ’c2’ => array(1, 2, 3),
5 new ZendDbSqlPredicateIsNotNull(’c3’)
6 ));
366 Chapter 85. ZendDbSql
Zend Framework 2 Documentation, Release 2.3.1dev
85.3.5 order():
1 $select = new Select;
2 $select->order(’id DESC’); // produces ’id’ DESC
3
4 $select = new Select;
5 $select->order(’id DESC’)
6 ->order(’name ASC, age DESC’); // produces ’id’ DESC, ’name’ ASC, ’age’ DESC
7
8 $select = new Select;
9 $select->order(array(’name ASC’, ’age DESC’)); // produces ’name’ ASC, ’age’ DESC
85.3.6 limit() and offset():
1 $select = new Select;
2 $select->limit(5); // always takes an integer/numeric
3 $select->offset(10); // similarly takes an integer/numeric
85.4 ZendDbSqlInsert
The Insert API:
1 class Insert implements SqlInterface, PreparableSqlInterface
2 {
3 const VALUES_MERGE = ’merge’;
4 const VALUES_SET = ’set’;
5
6 public function __construct($table = null);
7 public function into($table);
8 public function columns(array $columns);
9 public function values(array $values, $flag = self::VALUES_SET);
10 }
Similarly to Select objects, the table can be set at construction time or via into().
85.4.1 columns():
1 $insert->columns(array(’foo’, ’bar’)); // set the valid columns
85.4.2 values():
1 // default behavior of values is to set the values
2 // successive calls will not preserve values from previous calls
3 $insert->values(array(
4 ’col_1’ => ’value1’,
5 ’col_2’ => ’value2’
6 ));
1 // merging values with previous calls
2 $insert->values(array(’col_2’ => ’value2’), $insert::VALUES_MERGE);
85.4. ZendDbSqlInsert 367
Zend Framework 2 Documentation, Release 2.3.1dev
85.5 ZendDbSqlUpdate
1 class Update
2 {
3 const VALUES_MERGE = ’merge’;
4 const VALUES_SET = ’set’;
5
6 public $where; // @param Where $where
7 public function __construct($table = null);
8 public function table($table);
9 public function set(array $values, $flag = self::VALUES_SET);
10 public function where($predicate, $combination = PredicatePredicateSet::OP_AND);
11 }
85.5.1 set():
1 $update->set(array(’foo’ => ’bar’, ’baz’ => ’bax’));
85.5.2 where():
See where section below.
85.6 ZendDbSqlDelete
1 class Delete
2 {
3 public $where; // @param Where $where
4 public function __construct($table = null);
5 public function from($table);
6 public function where($predicate, $combination = PredicatePredicateSet::OP_AND);
7 }
85.6.1 where():
See where section below.
85.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
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.
368 Chapter 85. ZendDbSql
Zend Framework 2 Documentation, Release 2.3.1dev
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 // Where & Having:
2 class Predicate extends PredicateSet
3 {
4 public $and;
5 public $or;
6 public $AND;
7 public $OR;
8 public $NEST;
9 public $UNNEST;
10
11 public function nest();
12 public function setUnnest(Predicate $predicate);
13 public function unnest();
14 public function equalTo($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightType = self::TYP
15 public function lessThan($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightType = self::TY
16 public function greaterThan($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightType = self:
17 public function lessThanOrEqualTo($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightType =
18 public function greaterThanOrEqualTo($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightTyp
19 public function like($identifier, $like);
20 public function literal($literal);
21 public function expression($expression, $parameter);
22 public function isNull($identifier);
23 public function isNotNull($identifier);
24 public function in($identifier, array $valueSet = array());
25 public function between($identifier, $minValue, $maxValue);
26
27
28 // Inherited From PredicateSet
29
30 public function addPredicate(PredicateInterface $predicate, $combination = null);
31 public function getPredicates();
32 public function orPredicate(PredicateInterface $predicate);
33 public function andPredicate(PredicateInterface $predicate);
34 public function getExpressionData();
35 public function count();
36 }
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:
85.7. ZendDbSqlWhere & ZendDbSqlHaving 369
Zend Framework 2 Documentation, Release 2.3.1dev
85.7.1 equalTo(), lessThan(), greaterThan(), lessThanOrEqualTo(), greaterThanOrE-
qualTo():
1 $where->equalTo(’id’, 5);
2
3 // same as the following workflow
4 $where->addPredicate(
5 new PredicateOperator($left, Operator::OPERATOR_EQUAL_TO, $right, $leftType, $rightType)
6 );
7
8 class Operator implements PredicateInterface
9 {
10 const OPERATOR_EQUAL_TO = ’=’;
11 const OP_EQ = ’=’;
12 const OPERATOR_NOT_EQUAL_TO = ’!=’;
13 const OP_NE = ’!=’;
14 const OPERATOR_LESS_THAN = ’<’;
15 const OP_LT = ’<’;
16 const OPERATOR_LESS_THAN_OR_EQUAL_TO = ’<=’;
17 const OP_LTE = ’<=’;
18 const OPERATOR_GREATER_THAN = ’>’;
19 const OP_GT = ’>’;
20 const OPERATOR_GREATER_THAN_OR_EQUAL_TO = ’>=’;
21 const OP_GTE = ’>=’;
22
23 public function __construct($left = null, $operator = self::OPERATOR_EQUAL_TO, $right = null, $le
24 public function setLeft($left);
25 public function getLeft();
26 public function setLeftType($type);
27 public function getLeftType();
28 public function setOperator($operator);
29 public function getOperator();
30 public function setRight($value);
31 public function getRight();
32 public function setRightType($type);
33 public function getRightType();
34 public function getExpressionData();
35 }
85.7.2 like($identifier, $like):
1 $where->like($identifier, $like):
2
3 // same as
4 $where->addPredicate(
5 new PredicateLike($identifier, $like)
6 );
7
8 // full API
9
10 class Like implements PredicateInterface
11 {
12 public function __construct($identifier = null, $like = null);
13 public function setIdentifier($identifier);
14 public function getIdentifier();
15 public function setLike($like);
370 Chapter 85. ZendDbSql
Zend Framework 2 Documentation, Release 2.3.1dev
16 public function getLike();
17 }
85.7.3 literal($literal);
1 $where->literal($literal);
2
3 // same as
4 $where->addPredicate(
5 new PredicateLiteral($literal)
6 );
7
8 // full API
9 class Literal implements ExpressionInterface, PredicateInterface
10 {
11 const PLACEHOLDER = ’?’;
12 public function __construct($literal = ’’);
13 public function setLiteral($literal);
14 public function getLiteral();
15 }
85.7.4 expression($expression, $parameter);
1 $where->expression($expression, $parameter);
2
3 // same as
4 $where->addPredicate(
5 new PredicateExpression($expression, $parameter)
6 );
7
8 // full API
9 class Expression implements ExpressionInterface, PredicateInterface
10 {
11 const PLACEHOLDER = ’?’;
12 public function __construct($expression = null, $valueParameter = null /*[, $valueParameter, ...
13 public function setExpression($expression);
14 public function getExpression();
15 public function setParameters($parameters);
16 public function getParameters();
17 public function setTypes(array $types);
18 public function getTypes();
19 }
85.7.5 isNull($identifier);
1 $where->isNull($identifier);
2
3 // same as
4 $where->addPredicate(
5 new PredicateIsNull($identifier)
6 );
7
8 // full API
85.7. ZendDbSqlWhere & ZendDbSqlHaving 371
Zend Framework 2 Documentation, Release 2.3.1dev
9 class IsNull implements PredicateInterface
10 {
11 public function __construct($identifier = null);
12 public function setIdentifier($identifier);
13 public function getIdentifier();
14 }
85.7.6 isNotNull($identifier);
1 $where->isNotNull($identifier);
2
3 // same as
4 $where->addPredicate(
5 new PredicateIsNotNull($identifier)
6 );
7
8 // full API
9 class IsNotNull implements PredicateInterface
10 {
11 public function __construct($identifier = null);
12 public function setIdentifier($identifier);
13 public function getIdentifier();
14 }
85.7.7 in($identifier, array $valueSet = array());
1 $where->in($identifier, array $valueSet = array());
2
3 // same as
4 $where->addPredicate(
5 new PredicateIn($identifier, $valueSet)
6 );
7
8 // full API
9 class In implements PredicateInterface
10 {
11 public function __construct($identifier = null, array $valueSet = array());
12 public function setIdentifier($identifier);
13 public function getIdentifier();
14 public function setValueSet(array $valueSet);
15 public function getValueSet();
16 }
85.7.8 between($identifier, $minValue, $maxValue);
1 $where->between($identifier, $minValue, $maxValue);
2
3 // same as
4 $where->addPredicate(
5 new PredicateBetween($identifier, $minValue, $maxValue)
6 );
7
8 // full API
372 Chapter 85. ZendDbSql
Zend Framework 2 Documentation, Release 2.3.1dev
9 class Between implements PredicateInterface
10 {
11 public function __construct($identifier = null, $minValue = null, $maxValue = null);
12 public function setIdentifier($identifier);
13 public function getIdentifier();
14 public function setMinValue($minValue);
15 public function getMinValue();
16 public function setMaxValue($maxValue);
17 public function getMaxValue();
18 public function setSpecification($specification);
19 }
85.7. ZendDbSqlWhere & ZendDbSqlHaving 373
Zend Framework 2 Documentation, Release 2.3.1dev
374 Chapter 85. ZendDbSql
CHAPTER 86
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 state-
ments, 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
86.1 Creating Tables
Like ZendDbSql objects, each statement type is represented by a class. For example, CREATE TABLE is mod-
eled 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 // or with table
6 $table = new DdlCreateTable(’bar’);
7
8 // optionally, as a temporary table
9 $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 use ZendDbSqlDdlColumn;
2 $table->addColumn(new ColumnInteger(’id’));
3 $table->addColumn(new ColumnVarchar(’name’, 255));
Beyond adding columns to a table, constraints can also be added:
375
Zend Framework 2 Documentation, Release 2.3.1dev
1 use ZendDbSqlDdlConstraint;
2 $table->addConstraint(new ConstraintPrimaryKey(’id’));
3 $table->addConstraint(
4 new ConstraintUniqueKey([’name’, ’foo’], ’my_unique_key’)
5 );
86.2 Altering Tables
Similarly to CreateTable, you may also instantiate AlterTable:
1 use ZendDbSqlDdl;
2
3 $table = new DdlAlterTable();
4
5 // or with table
6 $table = new DdlAlterTable(’bar’);
7
8 // optionally, as a temporary table
9 $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 use ZendDbSqlDdlColumn;
2 $table->changeColumn(’name’, ColumnVarchar(’new_name’, 50));
You may also drop existing columns or constraints:
1 $table->dropColumn(’foo’);
2 $table->dropConstraint(’my_index’);
86.3 Dropping Tables
To drop a table, create a DropTable statement object:
1 $drop = new DdlDropTable(’bar’);
86.4 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 in-
stance:
1 use ZendDbSqlSql;
2
3 // existence of $adapter is assumed
4 $sql = new Sql($adapter);
5
6 $adapter->query(
376 Chapter 86. ZendDbSqlDdl
Zend Framework 2 Documentation, Release 2.3.1dev
7 $sql->getSqlStringForSqlObject($ddl),
8 $adapter::QUERY_MODE_EXECUTE
9 );
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 state-
ment is not prepared, as many DDL statements on a variety of platforms cannot be prepared, only executed.
86.5 Currently Supported Data Types
These types exist in the ZendDbSqlDdlColumn namespace. Data types must implement
ZendDbSqlDdlColumnColumnInterface.
In alphabetical order:
Type Arguments For Construction
Blob $name, $length, $nullable = false, $default = null, array
$options = array()
Boolean $name
Char $name, $length
Column
(generic)
$name = null
Date $name
Decimal $name, $precision, $scale = null
Float $name, $digits, $decimal
Integer $name, $nullable = false, $default = null, array $options =
array()
Time $name
Varchar $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.
86.6 Currently Supported Constraint Types
These types exist in the ZendDbSqlDdlConstraint namespace. Data types must implement
ZendDbSqlDdlConstraintConstraintInterface.
In alphabetical order:
Type Arguments For Construction
Check $expression, $name
For-
eignKey
$name, $column, $referenceTable, $referenceColumn, $onDeleteRule =
null, $onUpdateRule = null
Prima-
ryKey
$columns
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().
86.5. Currently Supported Data Types 377
Zend Framework 2 Documentation, Release 2.3.1dev
378 Chapter 86. ZendDbSqlDdl
CHAPTER 87
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 interface ZendDbTableGatewayTableGatewayInterface
2 {
3 public function getTable();
4 public function select($where = null);
5 public function insert($set);
6 public function update($set, $where = null);
7 public function delete($where);
8 }
There are two primary implementations of the TableGatewayInterface that are of the most useful:
AbstractTableGateway and TableGateway. The AbstractTableGateway is an abstract basic imple-
mentation that provides functionality for select(), insert(), update(), delete(), as well as an addi-
tional 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.
87.1 Basic Usage
The quickest way to get up and running with ZendDbTableGateway is to configure and utilize the concrete imple-
mentation of the TableGateway. The API of the concrete TableGateway is:
1 class TableGateway extends AbstractTableGateway
2 {
3 public $lastInsertValue;
4 public $table;
5 public $adapter;
6
7 public function __construct($table, Adapter $adapter, $features = null, ResultSet $resultSetProt
8
9 /** Inherited from AbstractTableGateway */
10
11 public function isInitialized();
12 public function initialize();
379
Zend Framework 2 Documentation, Release 2.3.1dev
13 public function getTable();
14 public function getAdapter();
15 public function getColumns();
16 public function getFeatureSet();
17 public function getResultSetPrototype();
18 public function getSql();
19 public function select($where = null);
20 public function selectWith(Select $select);
21 public function insert($set);
22 public function insertWith(Insert $insert);
23 public function update($set, $where = null);
24 public function updateWith(Update $update);
25 public function delete($where);
26 public function deleteWith(Delete $delete);
27 public function getLastInsertValue();
28 }
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 use ZendDbTableGatewayTableGateway;
2 $projectTable = new TableGateway(’project’, $adapter);
3 $rowset = $projectTable->select(array(’type’ => ’PHP’));
4
5 echo ’Projects of type PHP: ’;
6 foreach ($rowset as $projectRow) {
7 echo $projectRow[’name’] . PHP_EOL;
8 }
9
10 // or, when expecting a single row:
11 $artistTable = new TableGateway(’artist’, $adapter);
12 $rowset = $artistTable->select(array(’id’ => 2));
13 $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 use ZendDbTableGatewayTableGateway;
2 use ZendDbSqlSelect;
3 $artistTable = new TableGateway(’artist’, $adapter);
4
5 // search for at most 2 artists who’s name starts with Brit, ascending
6 $rowset = $artistTable->select(function (Select $select) {
7 $select->where->like(’name’, ’Brit%’);
8 $select->order(’name ASC’)->limit(2);
9 });
380 Chapter 87. ZendDbTableGateway
Zend Framework 2 Documentation, Release 2.3.1dev
87.2 TableGateway Features
The Features API allows for extending the functionality of the base TableGateway object without having to poly-
morphically 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 Fea-
tures 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 im-
plementation:
1 use ZendDbTableGatewayAbstractTableGateway;
2 use ZendDbTableGatewayFeature;
3
4 class MyTableGateway extends AbstractTableGateway
5 {
6 public function __construct()
7 {
8 $this->table = ’my_table’;
9 $this->featureSet = new FeatureFeatureSet();
10 $this->featureSet->addFeature(new FeatureGlobalAdapterFeature());
11 $this->initialize();
12 }
13 }
14
15 // elsewhere in code, in a bootstrap
16 ZendDbTableGatewayFeatureGlobalAdapterFeature::setStaticAdapter($adapter);
17
18 // in a controller, or model somewhere
19 $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 $table = new TableGateway(’artist’, $adapter, new FeatureRowGatewayFeature(’id’));
2 $results = $table->select(array(’id’ => 2));
3
4 $artistRow = $results->current();
87.2. TableGateway Features 381
Zend Framework 2 Documentation, Release 2.3.1dev
5 $artistRow->name = ’New Name’;
6 $artistRow->save();
382 Chapter 87. ZendDbTableGateway
CHAPTER 88
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 interface RowGatewayInterface
2 {
3 public function save();
4 public function delete();
5 }
88.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 // query the database
4 $resultSet = $adapter->query(’SELECT * FROM ‘user‘ WHERE ‘id‘ = ?’, array(2));
5
6 // get array of data
7 $rowData = $resultSet->current()->getArrayCopy();
8
9 // row gateway
10 $rowGateway = new RowGateway(’id’, ’my_table’, $adapter);
11 $rowGateway->populate($rowData);
12
13 $rowGateway->first_name = ’New Name’;
14 $rowGateway->save();
15
16 // or delete this row:
17 $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
383
Zend Framework 2 Documentation, Release 2.3.1dev
that is then capable of producing valid Row Gateway objects. Its usage looks like this:
1 use ZendDbTableGatewayFeatureRowGatewayFeature;
2 use ZendDbTableGatewayTableGateway;
3
4 $table = new TableGateway(’artist’, $adapter, new RowGatewayFeature(’id’));
5 $results = $table->select(array(’id’ => 2));
6
7 $artistRow = $results->current();
8 $artistRow->name = ’New Name’;
9 $artistRow->save();
88.2 ActiveRecord Style Objects
If you wish to have custom behaviour for your RowGateway objects (essentially making them behave simi-
larly to the ActiveRecord pattern), pass a prototype object implementing the RowGatewayInterface to the
RowGatewayFeature constructor instead of a primary key:
1 use ZendDbTableGatewayFeatureRowGatewayFeature;
2 use ZendDbTableGatewayTableGateway;
3 use ZendDbRowGatewayRowGatewayInterface;
4
5 class Artist implements RowGatewayInterface
6 {
7 protected $adapter;
8
9 public function __construct($adapter)
10 {
11 $this->adapter = $adapter;
12 }
13
14 // ... save() and delete() implementations
15 }
16
17 $table = new TableGateway(’artist’, $adapter, new RowGatewayFeature(new Artist($adapter)));
384 Chapter 88. ZendDbRowGateway
CHAPTER 89
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 interface MetadataInterface
2 {
3 public function getSchemas();
4
5 public function getTableNames($schema = null, $includeViews = false);
6 public function getTables($schema = null, $includeViews = false);
7 public function getTable($tableName, $schema = null);
8
9 public function getViewNames($schema = null);
10 public function getViews($schema = null);
11 public function getView($viewName, $schema = null);
12
13 public function getColumnNames($table, $schema = null);
14 public function getColumns($table, $schema = null);
15 public function getColumn($columnName, $table, $schema = null);
16
17 public function getConstraints($table, $schema = null);
18 public function getConstraint($constraintName, $table, $schema = null);
19 public function getConstraintKeys($constraint, $table, $schema = null);
20
21 public function getTriggerNames($schema = null);
22 public function getTriggers($schema = null);
23 public function getTrigger($triggerName, $schema = null);
24 }
89.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.
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.
385
Zend Framework 2 Documentation, Release 2.3.1dev
1 $metadata = new ZendDbMetadataMetadata($adapter);
2
3 // get the table names
4 $tableNames = $metadata->getTableNames();
5
6 foreach ($tableNames as $tableName) {
7 echo ’In Table ’ . $tableName . PHP_EOL;
8
9 $table = $metadata->getTable($tableName);
10
11
12 echo ’ With columns: ’ . PHP_EOL;
13 foreach ($table->getColumns() as $column) {
14 echo ’ ’ . $column->getName()
15 . ’ -> ’ . $column->getDataType()
16 . PHP_EOL;
17 }
18
19 echo PHP_EOL;
20 echo ’ With constraints: ’ . PHP_EOL;
21
22 foreach ($metadata->getConstraints($tableName) as $constraint) {
23 /** @var $constraint ZendDbMetadataObjectConstraintObject */
24 echo ’ ’ . $constraint->getName()
25 . ’ -> ’ . $constraint->getType()
26 . PHP_EOL;
27 if (!$constraint->hasColumns()) {
28 continue;
29 }
30 echo ’ column: ’ . implode(’, ’, $constraint->getColumns());
31 if ($constraint->isForeignKey()) {
32 $fkCols = array();
33 foreach ($constraint->getReferencedColumns() as $refColumn) {
34 $fkCols[] = $constraint->getReferencedTableName() . ’.’ . $refColumn;
35 }
36 echo ’ => ’ . implode(’, ’, $fkCols);
37 }
38 echo PHP_EOL;
39
40 }
41
42 echo ’----’ . PHP_EOL;
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 class ZendDbMetadataObjectTableObject
2 {
3 public function __construct($name);
4 public function setColumns(array $columns);
5 public function getColumns();
6 public function setConstraints($constraints);
7 public function getConstraints();
8 public function setName($name);
9 public function getName();
10 }
386 Chapter 89. ZendDbMetadata
Zend Framework 2 Documentation, Release 2.3.1dev
The ColumnObject:
1 class ZendDbMetadataObjectColumnObject {
2 public function __construct($name, $tableName, $schemaName = null);
3 public function setName($name);
4 public function getName();
5 public function getTableName();
6 public function setTableName($tableName);
7 public function setSchemaName($schemaName);
8 public function getSchemaName();
9 public function getOrdinalPosition();
10 public function setOrdinalPosition($ordinalPosition);
11 public function getColumnDefault();
12 public function setColumnDefault($columnDefault);
13 public function getIsNullable();
14 public function setIsNullable($isNullable);
15 public function isNullable();
16 public function getDataType();
17 public function setDataType($dataType);
18 public function getCharacterMaximumLength();
19 public function setCharacterMaximumLength($characterMaximumLength);
20 public function getCharacterOctetLength();
21 public function setCharacterOctetLength($characterOctetLength);
22 public function getNumericPrecision();
23 public function setNumericPrecision($numericPrecision);
24 public function getNumericScale();
25 public function setNumericScale($numericScale);
26 public function getNumericUnsigned();
27 public function setNumericUnsigned($numericUnsigned);
28 public function isNumericUnsigned();
29 public function getErratas();
30 public function setErratas(array $erratas);
31 public function getErrata($errataName);
32 public function setErrata($errataName, $errataValue);
33 }
The ConstraintObject:
1 class ZendDbMetadataObjectConstraintObject
2 {
3 public function __construct($name, $tableName, $schemaName = null);
4 public function setName($name);
5 public function getName();
6 public function setSchemaName($schemaName);
7 public function getSchemaName();
8 public function getTableName();
9 public function setTableName($tableName);
10 public function setType($type);
11 public function getType();
12 public function hasColumns();
13 public function getColumns();
14 public function setColumns(array $columns);
15 public function getReferencedTableSchema();
16 public function setReferencedTableSchema($referencedTableSchema);
17 public function getReferencedTableName();
18 public function setReferencedTableName($referencedTableName);
19 public function getReferencedColumns();
20 public function setReferencedColumns(array $referencedColumns);
21 public function getMatchOption();
89.1. Basic Usage 387
Zend Framework 2 Documentation, Release 2.3.1dev
22 public function setMatchOption($matchOption);
23 public function getUpdateRule();
24 public function setUpdateRule($updateRule);
25 public function getDeleteRule();
26 public function setDeleteRule($deleteRule);
27 public function getCheckClause();
28 public function setCheckClause($checkClause);
29 public function isPrimaryKey();
30 public function isUnique();
31 public function isForeignKey();
32 public function isCheck();
33
34 }
The TriggerObject:
1 class ZendDbMetadataObjectTriggerObject
2 {
3 public function getName();
4 public function setName($name);
5 public function getEventManipulation();
6 public function setEventManipulation($eventManipulation);
7 public function getEventObjectCatalog();
8 public function setEventObjectCatalog($eventObjectCatalog);
9 public function getEventObjectSchema();
10 public function setEventObjectSchema($eventObjectSchema);
11 public function getEventObjectTable();
12 public function setEventObjectTable($eventObjectTable);
13 public function getActionOrder();
14 public function setActionOrder($actionOrder);
15 public function getActionCondition();
16 public function setActionCondition($actionCondition);
17 public function getActionStatement();
18 public function setActionStatement($actionStatement);
19 public function getActionOrientation();
20 public function setActionOrientation($actionOrientation);
21 public function getActionTiming();
22 public function setActionTiming($actionTiming);
23 public function getActionReferenceOldTable();
24 public function setActionReferenceOldTable($actionReferenceOldTable);
25 public function getActionReferenceNewTable();
26 public function setActionReferenceNewTable($actionReferenceNewTable);
27 public function getActionReferenceOldRow();
28 public function setActionReferenceOldRow($actionReferenceOldRow);
29 public function getActionReferenceNewRow();
30 public function setActionReferenceNewRow($actionReferenceNewRow);
31 public function getCreated();
32 public function setCreated($created);
33 }
388 Chapter 89. ZendDbMetadata
CHAPTER 90
Dumping Variables
The static method ZendDebugDebug::dump() prints or returns information about an expression. This simple
technique of debugging is common because it is easy to use in an ad hoc fashion and requires no initialization, special
tools, or debugging environment.
90.1 Example of dump() method
1 ZendDebugDebug::dump($var, $label = null, $echo = true);
The $var argument specifies the expression or variable about which the ZendDebugDebug::dump() method
outputs information.
The $label argument is a string to be prepended to the output of ZendDebugDebug::dump(). It may be
useful, for example, to use labels if you are dumping information about multiple variables on a given screen.
The boolean $echo argument specifies whether the output of ZendDebugDebug::dump() is echoed or not. If
TRUE, the output is echoed. Regardless of the value of the $echo argument, the return value of this method contains
the output.
It may be helpful to understand that ZendDebugDebug::dump() method wraps the PHP function var_dump().
If the output stream is detected as a web presentation, the output of var_dump() is escaped using htmlspecialchars()
and wrapped with (X)HTML <pre> tags.
Tip: Debugging with ZendLog
Using ZendDebugDebug::dump() is best for ad hoc debugging during software development. You can add
code to dump a variable and then remove the code very quickly.
Also consider the ZendLog component when writing more permanent debugging code. For example, you can use the
DEBUG log level and the stream log writer to output the string returned by ZendDebugDebug::dump().
389
Zend Framework 2 Documentation, Release 2.3.1dev
390 Chapter 90. Dumping Variables
CHAPTER 91
Introduction to ZendDi
91.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.
91.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
391
Zend Framework 2 Documentation, Release 2.3.1dev
392 Chapter 91. Introduction to ZendDi
CHAPTER 92
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 namespace MyLibrary
2 {
3 class DbAdapter
4 {
5 protected $username = null;
6 protected $password = null;
7 public function __construct($username, $password)
8 {
9 $this->username = $username;
10 $this->password = $password;
11 }
12 }
13 }
14
15 namespace MyMovieApp
16 {
17 class MovieFinder
18 {
19 protected $dbAdapter = null;
20 public function __construct(MyLibraryDbAdapter $dbAdapter)
21 {
22 $this->dbAdapter = $dbAdapter;
23 }
24 }
25
26 class MovieLister
27 {
28 protected $movieFinder = null;
29 public function __construct(MovieFinder $movieFinder)
30 {
31 $this->movieFinder = $movieFinder;
32 }
33 }
34 }
With the above code, you find yourself writing the following to wire and utilize this code:
393
Zend Framework 2 Documentation, Release 2.3.1dev
1 // $config object is assumed
2
3 $dbAdapter = new MyLibraryDbAdapter($config->username, $config->password);
4 $movieFinder = new MyMovieAppMovieFinder($dbAdapter);
5 $movieLister = new MyMovieAppMovieLister($movieFinder);
6 foreach ($movieLister as $movie) {
7 // iterate and display $movie
8 }
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 candi-
date for using ZendDi. The usage is as simple as:
1 // inside a bootstrap somewhere
2 $di = new ZendDiDi();
3 $di->instanceManager()->setParameters(’MyLibraryDbAdapter’, array(
4 ’username’ => $config->username,
5 ’password’ => $config->password
6 ));
7
8 // inside each controller
9 $movieLister = $di->get(’MyMovieAppMovieLister’);
10 foreach ($movieLister as $movie) {
11 // iterate and display $movie
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:
1 public function __construct(DefinitionList $definitions = null, InstanceManager $instanceManager
2 {
3 $this->definitions = ($definitions) ?: new DefinitionList(new DefinitionRuntimeDefinition())
4 $this->instanceManager = ($instanceManager) ?: new InstanceManager();
5
6 if ($config) {
7 $this->configure($config);
8 }
9 }
This means that when $di->get() is called, it will be consulting the RuntimeDefinition, which uses reflection to un-
derstand 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():
1 // inside each controller
2 $di = new ZendDiDi();
3 $movieLister = $di->get(’MyMovieAppMovieLister’, array(
4 ’username’ => $config->username,
5 ’password’ => $config->password
6 ));
7 foreach ($movieLister as $movie) {
394 Chapter 92. ZendDi Quickstart
Zend Framework 2 Documentation, Release 2.3.1dev
8 // iterate and display $movie
9 }
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().
395
Zend Framework 2 Documentation, Release 2.3.1dev
396 Chapter 92. ZendDi Quickstart
CHAPTER 93
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.
93.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.
93.2 RuntimeDefinition
The default DefinitionList instantiated by ZendDiDi, when no other DefinitionList is provided, has as Defini-
tionRuntimeDefinition 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 public function __construct(IntrospectionStrategy $introspectionStrategy = null, array $explicitClass
2 {
3 $this->introspectionStrategy = ($introspectionStrategy) ?: new IntrospectionStrategy();
4 if ($explicitClasses) {
5 $this->setExplicitClasses($explicitClasses);
6 }
7 }
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:
• Whether or not to use Annotations (Annotations are expensive and off by default, read more about these in the
Annotations section)
397
Zend Framework 2 Documentation, Release 2.3.1dev
• 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 regis-
tered, this is a list of patterns.
The constructor for the IntrospectionStrategy looks like this:
1 public function __construct(AnnotationManager $annotationManager = null)
2 {
3 $this->annotationManager = ($annotationManager) ?: $this->createDefaultAnnotationManager();
4 }
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.
93.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 // in "package name" format
2 $components = array(
3 ’My_MovieApp’,
4 ’My_OtherClasses’,
5 );
6
7 foreach ($components as $component) {
8 $diCompiler = new ZendDiDefinitionCompilerDefinition;
9 $diCompiler->addDirectory(’/path/to/classes/’ . str_replace(’_’, ’/’, $component));
10
11 $diCompiler->compile();
12 file_put_contents(
13 __DIR__ . ’/../data/di/’ . $component . ’-definition.php’,
14 ’<?php return ’ . var_export($diCompiler->toArrayDefinition()->toArray(), true) . ’;’
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 protected function setupDi(Application $app)
2 {
3 $definitionList = new DefinitionList(array(
4 new DefinitionArrayDefinition(include __DIR__ . ’/path/to/data/di/My_MovieApp-definition.php
5 new DefinitionArrayDefinition(include __DIR__ . ’/path/to/data/di/My_OtherClasses-definition
6 $runtime = new DefinitionRuntimeDefinition(),
398 Chapter 93. ZendDi Definition
Zend Framework 2 Documentation, Release 2.3.1dev
7 ));
8 $di = new Di($definitionList, null, new Configuration($this->config->di));
9 $di->instanceManager()->addTypePreference(’ZendDiLocatorInterface’, $di);
10 $app->setLocator($di);
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.
93.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
93.4. ClassDefinition 399
Zend Framework 2 Documentation, Release 2.3.1dev
400 Chapter 93. ZendDi Definition
CHAPTER 94
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.
94.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 namespace MyLibrary
2 {
3 class DbAdapter
4 {
5 protected $username = null;
6 protected $password = null;
7 public function __construct($username, $password)
8 {
9 $this->username = $username;
10 $this->password = $password;
11 }
12 }
13 }
14
15 namespace MyMovieApp
16 {
17 class MovieFinder
18 {
19 protected $dbAdapter = null;
20 public function __construct(MyLibraryDbAdapter $dbAdapter)
21 {
22 $this->dbAdapter = $dbAdapter;
23 }
24 }
25
26 class MovieLister
27 {
28 protected $movieFinder = null;
401
Zend Framework 2 Documentation, Release 2.3.1dev
29 public function __construct(MovieFinder $movieFinder)
30 {
31 $this->movieFinder = $movieFinder;
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 // setting instance configuration into the instance manager
2 $di->instanceManager()->setParameters(’MyLibraryDbAdapter’, array(
3 ’username’ => ’myusername’,
4 ’password’ => ’mypassword’
5 ));
6
7 // forcing a particular dependency to be used by the instance manager
8 $di->instanceManager()->setParameters(’MyMovieAppMovieFinder’, array(
9 ’dbAdapter’ => new MyLibraryDbAdapter(’myusername’, ’mypassword’)
10 ));
11
12 // passing instance parameters at call time
13 $movieLister = $di->get(’MyMovieAppMovieLister’, array(
14 ’username’ => $config->username,
15 ’password’ => $config->password
16 ));
17
18 // passing a specific instance at call time
19 $movieLister = $di->get(’MyMovieAppMovieLister’, array(
20 ’dbAdapter’ => new MyLibraryDbAdapter(’myusername’, ’mypassword’)
21 ));
94.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 namespace MyMovieApp
2 {
3 interface MovieFinderInterface
4 {
5 // methods required for this type
6 }
7
8 class GenericMovieFinder implements MovieFinderInterface
9 {
10 protected $dbAdapter = null;
402 Chapter 94. ZendDi InstanceManager
Zend Framework 2 Documentation, Release 2.3.1dev
11 public function __construct(MyLibraryDbAdapter $dbAdapter)
12 {
13 $this->dbAdapter = $dbAdapter;
14 }
15 }
16
17 class MovieLister
18 {
19 protected $movieFinder = null;
20 public function __construct(MovieFinderInterface $movieFinder)
21 {
22 $this->movieFinder = $movieFinder;
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 $di->instanceManager()->addTypePreference(’MyMovieAppMovieFinderInterface’, ’MyMovieAppGenericMovie
2 // assuming all instance config for username, password is setup
3 $di->get(’MyMovieAppMovieLister’);
94.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 // add alias for short naming
6 $im->addAlias(’movielister’, ’MyMovieAppMovieLister’);
7
8 // add aliases for specific instances
9 $im->addAlias(’dbadapter-readonly’, ’MyLibraryDbAdapter’, array(
10 ’username’ => $config->db->readAdapter->username,
11 ’password’ => $config->db->readAdapter->password,
12 ));
13 $im->addAlias(’dbadapter-readwrite’, ’MyLibraryDbAdapter’, array(
14 ’username’ => $config->db->readWriteAdapter->username,
94.3. Aliases 403
Zend Framework 2 Documentation, Release 2.3.1dev
15 ’password’ => $config->db->readWriteAdapter->password,
16 ));
17
18 // set a default type to use, pointing to an alias
19 $im->addTypePreference(’MyLibraryDbAdapter’, ’dbadapter-readonly’);
20
21 $movieListerRead = $di->get(’MyMovieAppMovieLister’);
22 $movieListerReadWrite = $di->get(’MyMovieAppMovieLister’, array(’dbAdapter’ => ’dbadapter-readwrite’
404 Chapter 94. ZendDi InstanceManager
CHAPTER 95
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 $config = array(
2 ’definition’ => array(
3 ’compiler’ => array(/* @todo compiler information */),
4 ’runtime’ => array(/* @todo runtime information */),
5 ’class’ => array(
6 ’instantiator’ => ’’, // the name of the instantiator, by default this is __construct
7 ’supertypes’ => array(), // an array of supertypes the class implements
8 ’methods’ => array(
9 ’setSomeParameter’ => array( // a method name
10 ’parameterName’ => array(
11 ’name’, // string parameter name
12 ’type’, // type or null
13 ’is-required’ // bool
14 )
15 )
16
17 )
18 )
19 )
20 );
405
Zend Framework 2 Documentation, Release 2.3.1dev
406 Chapter 95. ZendDi Configuration
CHAPTER 96
ZendDi Debugging & Complex Use Cases
96.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’));
96.2 Complex Use Cases
96.2.1 Interface Injection
1 namespace FooBar {
2 class Baz implements BamAwareInterface
3 {
4 public $bam;
5
6 public function setBam(Bam $bam)
7 {
8 $this->bam = $bam;
9 }
10 }
11 class Bam
12 {
13 }
14 interface BamAwareInterface
15 {
16 public function setBam(Bam $bam);
17 }
18 }
19
20 namespace {
21 include ’zf2bootstrap.php’;
22 $di = new ZendDiDi;
407
Zend Framework 2 Documentation, Release 2.3.1dev
23 $baz = $di->get(’FooBarBaz’);
24 }
96.2.2 Setter Injection with Class Definition
1 namespace FooBar {
2 class Baz
3 {
4 public $bam;
5
6 public function setBam(Bam $bam)
7 {
8 $this->bam = $bam;
9 }
10 }
11 class Bam {
12 }
13 }
14
15 namespace {
16 $di = new ZendDiDi;
17 $di->configure(new ZendDiConfig(array(
18 ’definition’ => array(
19 ’class’ => array(
20 ’FooBarBaz’ => array(
21 ’setBam’ => array(’required’ => true)
22 )
23 )
24 )
25 )));
26 $baz = $di->get(’FooBarBaz’);
27 }
96.2.3 Multiple Injections To A Single Injection Point
1 namespace Application {
2 class Page
3 {
4 public $blocks;
5
6 public function addBlock(Block $block)
7 {
8 $this->blocks[] = $block;
9 }
10 }
11 interface Block
12 {
13 }
14 }
15
16 namespace MyModule {
17 class BlockOne implements ApplicationBlock {}
18 class BlockTwo implements ApplicationBlock {}
19 }
20
408 Chapter 96. ZendDi Debugging & Complex Use Cases
Zend Framework 2 Documentation, Release 2.3.1dev
21 namespace {
22 include ’zf2bootstrap.php’;
23 $di = new ZendDiDi;
24 $di->configure(new ZendDiConfig(array(
25 ’definition’ => array(
26 ’class’ => array(
27 ’ApplicationPage’ => array(
28 ’addBlock’ => array(
29 ’block’ => array(’type’ => ’ApplicationBlock’, ’required’ => true)
30 )
31 )
32 )
33 ),
34 ’instance’ => array(
35 ’ApplicationPage’ => array(
36 ’injections’ => array(
37 ’MyModuleBlockOne’,
38 ’MyModuleBlockTwo’
39 )
40 )
41 )
42 )));
43 $page = $di->get(’ApplicationPage’);
44 }
96.2. Complex Use Cases 409
Zend Framework 2 Documentation, Release 2.3.1dev
410 Chapter 96. ZendDi Debugging & Complex Use Cases
CHAPTER 97
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.
411
Zend Framework 2 Documentation, Release 2.3.1dev
412 Chapter 97. Introduction to ZendDom
CHAPTER 98
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 (Proto-
type’s $$() and Dojo’s dojo.query were both inspirations for the component).
98.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.
• 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’
413
Zend Framework 2 Documentation, Release 2.3.1dev
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:
1 <div>
2 <table>
3 <tr>
4 <td class="foo">
5 <div>
6 Lorem ipsum <span class="bar">
7 <a href="/foo/bar" id="one">One</a>
8 <a href="/foo/baz" id="two">Two</a>
9 <a href="/foo/bat" id="three">Three</a>
10 <a href="/foo/bla" id="four">Four</a>
11 </span>
12 </div>
13 </td>
14 </tr>
15 </table>
16 </div>
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 $dom = new Query($html);
4 $results = $dom->execute(’.foo .bar a’);
5
6 $count = count($results); // get number of matches: 4
7 foreach ($results as $result) {
8 // $result is a DOMElement
9 }
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.
98.2 Methods Available
The ZendDomQuery family of classes have the following methods available.
98.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 a string to query against;
ZendDomQuery will then attempt to autodetect the document type.
414 Chapter 98. ZendDomQuery
Zend Framework 2 Documentation, Release 2.3.1dev
• setEncoding($encoding): specify an encoding string to use. This encoding will be passed to DOMDoc-
ument’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.
98.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.
98.2. Methods Available 415
Zend Framework 2 Documentation, Release 2.3.1dev
416 Chapter 98. ZendDomQuery
CHAPTER 99
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.
99.1 Overview
The ZendEscaper component provides one class, ZendEscaperEscaper which in turn, provides five meth-
ods 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.
99.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.
417
Zend Framework 2 Documentation, Release 2.3.1dev
418 Chapter 99. Introduction to ZendEscaper
CHAPTER 100
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 // &lt;script&gt;alert(&quot;zf2&quot;)&lt;/script&gt;
4 echo $escaper->escapeHtml(’<script>alert("zf2")</script>’);
5 // &lt;script&gt;alert&#x28;&quot;zf2&quot;&#x29;&lt;&#x2F;script&gt;
6 echo $escaper->escapeHtmlAttr(’<script>alert("zf2")</script>’);
7 // x3Cscriptx3Ealertx28x22zf2x22x29x3Cx2Fscriptx3E
8 echo $escaper->escapeJs(’<script>alert("zf2")</script>’);
9 // 3C script3E alert28 22 zf222 29 3C 2F script3E
10 echo $escaper->escapeCss(’<script>alert("zf2")</script>’);
11 // %3Cscript%3Ealert%28%22zf2%22%29%3C%2Fscript%3E
12 echo $escaper->escapeUrl(’<script>alert("zf2")</script>’);
More detailed examples will be given in later chapters.
100.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()
• Javascript: addslashes() or json_encode()
• CSS: n/a
419
Zend Framework 2 Documentation, Release 2.3.1dev
• 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 misrepre-
sentations 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.
100.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!):
100.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.
100.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!
420 Chapter 100. Theory of Operation
Zend Framework 2 Documentation, Release 2.3.1dev
100.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).
100.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.
100.2. Why Contextual Escaping? 421
Zend Framework 2 Documentation, Release 2.3.1dev
422 Chapter 100. Theory of Operation
CHAPTER 101
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, a
ZendEscaperExceptionInvalidArgumentException will be thrown.
423
Zend Framework 2 Documentation, Release 2.3.1dev
424 Chapter 101. Configuring ZendEscaper
CHAPTER 102
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 // outputting this without escaping would be a bad idea!
2 $input = ’<script>alert("zf2")</script>’;
3
4 $escaper = new ZendEscaperEscaper(’utf-8’);
5
6 // somewhere in an HTML template
7 <div class="user-provided-input">
8 <?php
9 echo $escaper->escapeHtml($input); // all safe!
10 ?>
11 </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!
102.1 Examples of Bad HTML Escaping
An example of incorrect usage:
1 <?php
2 $input = ’<script>alert("zf2")</script>’;
3 $escaper = new ZendEscaperEscaper(’utf-8’);
4 ?>
5 <?php header(’Content-Type: text/html; charset=ISO-8859-1’); ?>
6 <!DOCTYPE html>
7 <html>
8 <head>
9 <title>Encodings set incorrectly!</title>
10 <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
11 </head>
12 <body>
13 <?php
14 // Bad! The escaper’s and the document’s encodings are different!
15 echo $escaper->escapeHtml($input);
425
Zend Framework 2 Documentation, Release 2.3.1dev
16 ?>
17 </body>
102.2 Examples of Good HTML Escaping
An example of correct usage:
1 <?php
2 $input = ’<script>alert("zf2")</script>’;
3 $escaper = new ZendEscaperEscaper(’utf-8’);
4 ?>
5 <?php header(’Content-Type: text/html; charset=UTF-8’); ?>
6 <!DOCTYPE html>
7 <html>
8 <head>
9 <title>Encodings set correctly!</title>
10 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
11 </head>
12 <body>
13 <?php
14 // Good! The escaper’s and the document’s encodings are same!
15 echo $escaper->escapeHtml($input);
16 ?>
17 </body>
426 Chapter 102. Escaping HTML
CHAPTER 103
Escaping HTML Attributes
Escaping data in the HTML Attribute context is most often done incorrectly, if not overlooked completely by de-
velopers. 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.
103.1 Examples of Bad HTML Attribute Escaping
An example of incorrect HTML attribute escaping:
1 <?php header(’Content-Type: text/html; charset=UTF-8’); ?>
2 <!DOCTYPE html>
3 <?php
4 $input = <<<INPUT
5 ’ onmouseover=’alert(/ZF2!/);
6 INPUT;
7 /**
8 * NOTE: This is equivalent to using htmlspecialchars($input, ENT_COMPAT)
9 */
10 $output = htmlspecialchars($input);
11 ?>
12 <html>
13 <head>
14 <title>Single Quoted Attribute</title>
15 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
16 </head>
17 <body>
18 <div>
19 <?php
20 // the span tag will look like:
21 // <span title=’’ onmouseover=’alert(/ZF2!/);’>
22 ?>
23 <span title=’<?php echo $output ?>’>
24 What framework are you using?
25 </span>
26 </div>
27 </body>
28 </html>
427
Zend Framework 2 Documentation, Release 2.3.1dev
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 <?php header(’Content-Type: text/html; charset=UTF-8’); ?>
2 <!DOCTYPE html>
3 <?php
4 $input = <<<INPUT
5 faketitle onmouseover=alert(/ZF2!/);
6 INPUT;
7 // Tough luck using proper flags when the title attribute is unquoted!
8 $output = htmlspecialchars($input,ENT_QUOTES);
9 ?>
10 <html>
11 <head>
12 <title>Quoteless Attribute</title>
13 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
14 </head>
15 <body>
16 <div>
17 <?php
18 // the span tag will look like:
19 // <span title=faketitle onmouseover=alert(/ZF2!/);>
20 ?>
21 <span title=<?php echo $output ?>>
22 What framework are you using?
23 </span>
24 </div>
25 </body>
26 </html>
The above example shows how it is easy to break out from unquoted attributes in HTML5.
103.2 Examples of Good HTML Attribute Escaping
Both of the previous examples can be avoided by simply using the escapeHtmlAttr method:
1 <?php header(’Content-Type: text/html; charset=UTF-8’); ?>
2 <!DOCTYPE html>
3 <?php
4 $input = <<<INPUT
5 faketitle onmouseover=alert(/ZF2!/);
6 INPUT;
7 $escaper = new ZendEscaperEscaper(’utf-8’);
8 $output = $escaper->escapeHtmlAttr($input);
9 ?>
10 <html>
11 <head>
12 <title>Quoteless Attribute</title>
13 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
14 </head>
15 <body>
16 <div>
17 <?php
18 // the span tag will look like:
428 Chapter 103. Escaping HTML Attributes
Zend Framework 2 Documentation, Release 2.3.1dev
19 // <span title=faketitle&#x20;onmouseover&#x3D;alert&#x28;&#x2F;ZF2&#x21;&#x2F;&#x29;&#x3B;>
20 ?>
21 <span title=<?php echo $output ?>>
22 What framework are you using?
23 </span>
24 </div>
25 </body>
26 </html>
In the above example, the malicious input from the attacker becomes completely harmless as we used proper HTML
attribute escaping!
103.2. Examples of Good HTML Attribute Escaping 429
Zend Framework 2 Documentation, Release 2.3.1dev
430 Chapter 103. Escaping HTML Attributes
CHAPTER 104
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.
104.1 Examples of Bad Javascript Escaping
An example of incorrect Javascript escaping:
1 <?php header(’Content-Type: application/xhtml+xml; charset=UTF-8’); ?>
2 <!DOCTYPE html>
3 <?php
4 $input = <<<INPUT
5 bar&quot;; alert(&quot;Meow!&quot;); var xss=&quot;true
6 INPUT;
7 $output = json_encode($input);
8 ?>
9 <html xmlns="http://www.w3.org/1999/xhtml">
10 <head>
11 <title>Unescaped Entities</title>
12 <meta charset="UTF-8"/>
13 <script type="text/javascript">
14 <?php
15 // this will result in
16 // var foo = "bar&quot;; alert(&quot;Meow!&quot;); var xss=&quot;true";
17 ?>
18 var foo = <?php echo $output ?>;
19 </script>
20 </head>
21 <body>
22 <p>json_encode() is not good for escaping javascript!</p>
23 </body>
24 </html>
431
Zend Framework 2 Documentation, Release 2.3.1dev
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.
104.2 Examples of Good Javascript Escaping
By using the escapeJs method in the Javascript context, such attacks can be prevented:
1 <?php header(’Content-Type: text/html; charset=UTF-8’); ?>
2 <!DOCTYPE html>
3 <?php
4 $input = <<<INPUT
5 bar&quot;; alert(&quot;Meow!&quot;); var xss=&quot;true
6 INPUT;
7 $escaper = new ZendEscaperEscaper(’utf-8’);
8 $output = $escaper->escapeJs($input);
9 ?>
10 <html xmlns="http://www.w3.org/1999/xhtml">
11 <head>
12 <title>Escaped Entities</title>
13 <meta charset="UTF-8"/>
14 <script type="text/javascript">
15 <?php
16 // this will look like
17 // var foo = barx26quotx3Bx3Bx20alertx28x26quotx3BMeowx21x26quotx3Bx29x3Bx20var
18 ?>
19 var foo = <?php echo $output ?>;
20 </script>
21 </head>
22 <body>
23 <p>ZendEscaperEscaper::escapeJs() is good for escaping javascript!</p>
24 </body>
25 </html>
In the above example, the Javascript parser will most likely report a SyntaxError, but at least the targeted applica-
tion remains safe from such attacks.
432 Chapter 104. Escaping Javascript
CHAPTER 105
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.
105.1 Examples of Bad CSS Escaping
In most cases developers forget to escape CSS completely:
1 <?php header(’Content-Type: application/xhtml+xml; charset=UTF-8’); ?>
2 <!DOCTYPE html>
3 <?php
4 $input = <<<INPUT
5 body {
6 background-image: url(’http://example.com/foo.jpg?</style><script>alert(1)</script>’);
7 }
8 INPUT;
9 ?>
10 <html xmlns="http://www.w3.org/1999/xhtml">
11 <head>
12 <title>Unescaped CSS</title>
13 <meta charset="UTF-8"/>
14 <style>
15 <?php echo $input; ?>
16 </style>
17 </head>
18 <body>
19 <p>User controlled CSS needs to be properly escaped!</p>
20 </body>
21 </html>
In the above example, by failing to escape the user provided CSS, an attacker can execute an XSS attack fairly easily.
105.2 Examples of Good CSS Escaping
By using escapeCss method in the CSS context, such attacks can be prevented:
1 <?php header(’Content-Type: application/xhtml+xml; charset=UTF-8’); ?>
2 <!DOCTYPE html>
3 <?php
4 $input = <<<INPUT
433
Zend Framework 2 Documentation, Release 2.3.1dev
5 body {
6 background-image: url(’http://example.com/foo.jpg?</style><script>alert(1)</script>’);
7 }
8 INPUT;
9 $escaper = new ZendEscaperEscaper(’utf-8’);
10 $output = $escaper->escapeCss($input);
11 ?>
12 <html xmlns="http://www.w3.org/1999/xhtml">
13 <head>
14 <title>Escaped CSS</title>
15 <meta charset="UTF-8"/>
16 <style>
17 <?php
18 // output will look something like
19 // body20 7B A 20 20 20 20 background2D image3A 20 url28 ...
20 echo $output;
21 ?>
22 </style>
23 </head>
24 <body>
25 <p>User controlled CSS needs to be properly escaped!</p>
26 </body>
27 </html>
By properly escaping user controlled CSS, we can prevent XSS attacks in our web applications.
434 Chapter 105. Escaping Cascading Style Sheets
CHAPTER 106
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.
106.1 Examples of Bad URL Escaping
XSS attacks are easy if data inserted into URLs is not escaped properly:
1 <?php header(’Content-Type: application/xhtml+xml; charset=UTF-8’); ?>
2 <!DOCTYPE html>
3 <?php
4 $input = <<<INPUT
5 " onmouseover="alert(’zf2’)
6 INPUT;
7 ?>
8 <html xmlns="http://www.w3.org/1999/xhtml">
9 <head>
10 <title>Unescaped URL data</title>
11 <meta charset="UTF-8"/>
12 </head>
13 <body>
14 <a href="http://example.com/?name=<?php echo $input; ?>">Click here!</a>
15 </body>
16 </html>
106.2 Examples of Good URL Escaping
By properly escaping data in URLs by using escapeUrl, we can prevent XSS attacks:
1 <?php header(’Content-Type: application/xhtml+xml; charset=UTF-8’); ?>
2 <!DOCTYPE html>
3 <?php
4 $input = <<<INPUT
5 " onmouseover="alert(’zf2’)
6 INPUT;
7 $escaper = new ZendEscaperEscaper(’utf-8’);
8 $output = $escaper->escapeUrl($input);
9 ?>
435
Zend Framework 2 Documentation, Release 2.3.1dev
10 <html xmlns="http://www.w3.org/1999/xhtml">
11 <head>
12 <title>Unescaped URL data</title>
13 <meta charset="UTF-8"/>
14 </head>
15 <body>
16 <a href="http://example.com/?name=<?php echo $output; ?>">Click here!</a>
17 </body>
18 </html>
436 Chapter 106. Escaping URLs
CHAPTER 107
The EventManager
107.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.
107.2 Quick Start
Typically, you will compose an EventManager instance in a class.
1 use ZendEventManagerEventManagerInterface;
2 use ZendEventManagerEventManager;
3 use ZendEventManagerEventManagerAwareInterface;
4
5 class Foo implements EventManagerAwareInterface
6 {
7 protected $events;
8
9 public function setEventManager(EventManagerInterface $events)
10 {
11 $events->setIdentifiers(array(
12 __CLASS__,
13 get_called_class(),
14 ));
15 $this->events = $events;
16 return $this;
17 }
18
19 public function getEventManager()
20 {
21 if (null === $this->events) {
22 $this->setEventManager(new EventManager());
23 }
24 return $this->events;
437
Zend Framework 2 Documentation, Release 2.3.1dev
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 class Foo
2 {
3 // ... assume events definition from above
4
5 public function bar($baz, $bat = null)
6 {
7 $params = compact(’baz’, ’bat’);
8 $this->getEventManager()->trigger(__FUNCTION__, $this, $params);
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 $log = LogFactory($someConfig);
4 $foo = new Foo();
5 $foo->getEventManager()->attach(’bar’, function ($e) use ($log) {
6 $event = $e->getName();
7 $target = get_class($e->getTarget());
8 $params = json_encode($e->getParams());
9
10 $log->info(sprintf(
11 ’%s called on %s, using params %s’,
12 $event,
13 $target,
14 $params
15 ));
16 });
17
18 // Results in log message:
19 $foo->bar(’baz’, ’bat’);
20 // 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 they do to normal event managers; the call to attach is identical to the EventManager,
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 con-
structor, may be used to identify an instance when using a SharedEventCollection. As an example, assuming
438 Chapter 107. The EventManager
Zend Framework 2 Documentation, Release 2.3.1dev
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 $log = LogFactory($someConfig);
6 $events->attach(’Foo’, ’bar’, function ($e) use ($log) {
7 $event = $e->getName();
8 $target = get_class($e->getTarget());
9 $params = json_encode($e->getParams());
10
11 $log->info(sprintf(
12 ’%s called on %s, using params %s’,
13 $event,
14 $target,
15 $params
16 ));
17 });
18
19 // Later, instantiate Foo:
20 $foo = new Foo();
21 $foo->getEventManager()->setSharedManager($events);
22
23 // And we can still trigger the above event:
24 $foo->bar(’baz’, ’bat’);
25 // results in log message:
26 // 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.
107.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.
Attaching to many events at once
1 $events = new EventManager();
2 $events->attach(array(’these’, ’are’, ’event’, ’names’), $callback);
Note that if you specify a priority, that priority will be used for all events specified.
107.2. Quick Start 439
Zend Framework 2 Documentation, Release 2.3.1dev
Attaching using the wildcard
1 $events = new EventManager();
2 $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 $events = new SharedEventManager();
2 // Attach to many events on the context "foo"
3 $events->attach(’foo’, array(’these’, ’are’, ’event’, ’names’), $callback);
4
5 // Attach to many events on the contexts "foo" and "bar"
6 $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 $events = new SharedEventManager();
2 // Attach to all events on the context "foo"
3 $events->attach(’foo’, ’*’, $callback);
4
5 // Attach to all events on the contexts "foo" and "bar"
6 $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.
107.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.
107.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.
440 Chapter 107. The EventManager
Zend Framework 2 Documentation, Release 2.3.1dev
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 as-
sociative 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.
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.
107.4. Available Methods 441
Zend Framework 2 Documentation, Release 2.3.1dev
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.
107.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 class ValueObject
2 {
3 // assume a composed event manager
4
5 function inject(array $values)
6 {
7 $argv = compact(’values’);
8 $argv = $this->getEventManager()->prepareArgs($argv);
9 $this->getEventManager()->trigger(__FUNCTION__, $this, $argv);
10 $date = isset($argv[’values’][’date’]) ? $argv[’values’][’date’] : new DateTime(’now’);
11
12 // ...
13 }
14 }
15
16 $v = new ValueObject();
17
18 $v->getEventManager()->attach(’inject’, function($e) {
19 $values = $e->getParam(’values’);
20 if (!$values) {
21 return;
22 }
23
24 if (!isset($values[’date’])) {
25 $values[’date’] = new DateTime(’now’);
26 } else {
27 $values[’date’] = new Datetime($values[’date’]);
28 }
29
30 $e->setParam(’values’, $values);
31 });
32
442 Chapter 107. The EventManager
Zend Framework 2 Documentation, Release 2.3.1dev
33 $v->inject(array(
34 ’date’ => ’2011-08-10 15:30:29’,
35 ));
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 $listener = function($e) {
2 // do some work
3
4 // Stop propagation and return a response
5 $e->stopPropagation(true);
6 return $response;
7 };
Alternately, we could do the check from the method triggering the event.
1 class Foo implements DispatchableInterface
2 {
3 // assume composed event manager
4
5 public function dispatch(Request $request, Response $response = null)
6 {
7 $argv = compact(’request’, ’response’);
8 $results = $this->getEventManager()->triggerUntil(__FUNCTION__, $this, $argv, function($v) {
9 return ($v instanceof Response);
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 class Foo implements DispatchableInterface
2 {
3 // assume composed event manager
4
5 public function dispatch(Request $request, Response $response = null)
6 {
7 $argv = compact(’request’, ’response’);
8 $results = $this->getEventManager()->triggerUntil(__FUNCTION__, $this, $argv, function($v) {
9 return ($v instanceof Response);
10 });
11
12 // Test if execution was halted, and return last result:
13 if ($results->stopped()) {
14 return $results->last();
15 }
16
17 // continue...
18 }
19 }
107.5. Examples 443
Zend Framework 2 Documentation, Release 2.3.1dev
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 class SomeValueObject
2 {
3 // assume it composes an event manager
4
5 public function get($id)
6 {
7 $params = compact(’id’);
8 $results = $this->getEventManager()->trigger(’get.pre’, $this, $params);
9
10 // If an event stopped propagation, return the value
11 if ($results->stopped()) {
12 return $results->last();
13 }
14
15 // do some work...
16
17 $params[’__RESULT__’] = $someComputedContent;
18 $this->getEventManager()->trigger(’get.post’, $this, $params);
19 }
20 }
Now, let’s create a ListenerAggregateInterface that can handle caching for us:
1 use ZendCacheCache;
2 use ZendEventManagerEventManagerInterface;
3 use ZendEventManagerListenerAggregateInterface;
4 use ZendEventManagerEventInterface;
5
6 class CacheListener implements ListenerAggregateInterface
7 {
8 protected $cache;
9
10 protected $listeners = array();
11
12 public function __construct(Cache $cache)
13 {
14 $this->cache = $cache;
15 }
16
17 public function attach(EventManagerInterface $events)
18 {
19 $this->listeners[] = $events->attach(’get.pre’, array($this, ’load’), 100);
20 $this->listeners[] = $events->attach(’get.post’, array($this, ’save’), -100);
21 }
22
444 Chapter 107. The EventManager
Zend Framework 2 Documentation, Release 2.3.1dev
23 public function detach(EventManagerInterface $events)
24 {
25 foreach ($this->listeners as $index => $listener) {
26 if ($events->detach($listener)) {
27 unset($this->listeners[$index]);
28 }
29 }
30 }
31
32 public function load(EventInterface $e)
33 {
34 $id = get_class($e->getTarget()) . ’-’ . json_encode($e->getParams());
35 if (false !== ($content = $this->cache->load($id))) {
36 $e->stopPropagation(true);
37 return $content;
38 }
39 }
40
41 public function save(EventInterface $e)
42 {
43 $params = $e->getParams();
44 $content = $params[’__RESULT__’];
45 unset($params[’__RESULT__’]);
46
47 $id = get_class($e->getTarget()) . ’-’ . json_encode($params);
48 $this->cache->save($content, $id);
49 }
50 }
We can then attach the aggregate to an instance.
1 $value = new SomeValueObject();
2 $cacheListener = new CacheListener($cache);
3 $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.
107.5. Examples 445
Zend Framework 2 Documentation, Release 2.3.1dev
446 Chapter 107. The EventManager
CHAPTER 108
Using Exceptions
Zend_Exception is simply the base class for all exceptions thrown within Zend Framework.
108.1 Catching an Exception
The following code listing demonstrates how to catch an exception thrown in a Zend Framework class:
1 try {
2 // Calling ZendLoaderLoader::loadClass() with a non-existent class will cause
3 // an exception to be thrown in ZendLoaderLoader:
4 ZendLoaderLoader::loadClass(’NonExistentClass’);
5 } catch (Zend_Exception $e) {
6 echo "Caught exception: " . get_class($e) . "n";
7 echo "Message: " . $e->getMessage() . "n";
8 // Other code to recover from the error
9 }
Zend_Exception can be used as a catch-all exception class in a catch block to trap all exceptions thrown by Zend
Framework classes. This can be useful when the program can not recover by catching a specific exception type.
The documentation for each Zend Framework component and class will contain specific information on which methods
throw exceptions, the circumstances that cause an exception to be thrown, and the various exception types that may be
thrown.
447
Zend Framework 2 Documentation, Release 2.3.1dev
448 Chapter 108. Using Exceptions
CHAPTER 109
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 writ-
ing 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.
109.1 Reading RSS Feed Data
1 // Fetch the latest Slashdot headlines
2 try {
3 $slashdotRss =
4 ZendFeedReaderReader::import(’http://rss.slashdot.org/Slashdot/slashdot’);
5 } catch (ZendFeedReaderExceptionRuntimeException $e) {
6 // feed import failed
7 echo "Exception caught importing feed: {$e->getMessage()}n";
8 exit;
9 }
10
11 // Initialize the channel/feed data array
12 $channel = array(
13 ’title’ => $slashdotRss->getTitle(),
14 ’link’ => $slashdotRss->getLink(),
15 ’description’ => $slashdotRss->getDescription(),
16 ’items’ => array()
17 );
18
19 // Loop over each channel item/entry and store relevant data for each
449
Zend Framework 2 Documentation, Release 2.3.1dev
20 foreach ($slashdotRss as $item) {
21 $channel[’items’][] = array(
22 ’title’ => $item->getTitle(),
23 ’link’ => $item->getLink(),
24 ’description’ => $item->getDescription()
25 );
26 }
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.
450 Chapter 109. Introduction to ZendFeed
CHAPTER 110
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 // importing a feed from a text file
2 $feedFromFile = ZendFeedReaderReader::importFile(’feed.xml’);
3
4 // importing a feed from a PHP string variable
5 $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.
110.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 // dump the feed to standard output
4 print $feed->saveXml();
451
Zend Framework 2 Documentation, Release 2.3.1dev
452 Chapter 110. Importing Feeds
CHAPTER 111
Retrieving Feeds from Web Pages
111.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 $rssFeed = null;
2 $feedLinks = ZendFeedReaderReader::findFeedLinks(’http://www.example.com/news.html’);
3 foreach ($feedLinks as $link) {
4 if (stripos($link[’type’], ’application/rss+xml’) !== false) {
5 $rssFeed = $link[’href’];
6 break;
7 }
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 $rssFeed = null;
2 $feedLinks = ZendFeedReaderReader::findFeedLinks(’http://www.example.com/news.html’);
3 $firstAtomFeed = $feedLinks->atom;
453
Zend Framework 2 Documentation, Release 2.3.1dev
454 Chapter 111. Retrieving Feeds from Web Pages
CHAPTER 112
Consuming an RSS Feed
112.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.
112.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 $author = $channel->getAuthor();
2 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 foreach ($channel as $item) {
2 echo $item->getTitle() . "n";
3 }
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).
Required channel elements:
• title- The name of the channel
455
Zend Framework 2 Documentation, Release 2.3.1dev
• 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 if ($item->getPropname()) {
2 // ... proceed.
3 }
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.
Please see the official RSS 2.0 specification for further information.
456 Chapter 112. Consuming an RSS Feed
CHAPTER 113
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.
113.1 Basic Use of an Atom Feed
Read an Atom feed and print the title and summary of each entry:
1 $feed = ZendFeedReaderReader::import(’http://atom.example.com/feed/’);
2 echo ’The feed contains ’ . $feed->count() . ’ entries.’ . "nn";
3 foreach ($feed as $entry) {
4 echo ’Title: ’ . $entry->getTitle() . "n";
5 echo ’Description: ’ . $entry->getDescription() . "n";
6 echo ’URL: ’ . $entry->getLink() . "nn";
7 }
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
The link property of an atom entry typically has an href attribute.
• summary- A summary of this entry’s content
457
Zend Framework 2 Documentation, Release 2.3.1dev
• 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/.
458 Chapter 113. Consuming an Atom Feed
CHAPTER 114
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.
114.1 Reading a Single-Entry Atom Feed
1 $entry = ZendFeedReaderReader::import(’http://atom.example.com/feed/1’);
2 echo ’Entry title: ’ . $entry->getTitle();
459
Zend Framework 2 Documentation, Release 2.3.1dev
460 Chapter 114. Consuming a Single Atom Entry
CHAPTER 115
ZendFeed and Security
115.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 appli-
cation or before storing that data in some storage engine (be it a simple file, a database...).
115.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 standards-
compliant output.
The following examples will show a basic usage of HTMLPurifier, but developers are urged to go through and read
HTMLPurifier’s documentation.
1 // Setting HTMLPurifier’s options
2 $options = array(
3 // Allow only paragraph tags
4 // and anchor tags wit the href attribute
5 array(
6 ’HTML.Allowed’,
7 ’p,a[href]’
8 ),
9 // Format end output with Tidy
10 array(
11 ’Output.TidyFormat’,
12 true
13 ),
14 // Assume XHTML 1.0 Strict Doctype
15 array(
16 ’HTML.Doctype’,
1 http://tools.ietf.org/html/rfc4287#section-8.1
461
Zend Framework 2 Documentation, Release 2.3.1dev
17 ’XHTML 1.0 Strict’
18 ),
19 // Disable cache, but see note after the example
20 array(
21 ’Cache.DefinitionImpl’,
22 null
23 )
24 );
25
26 // Configuring HTMLPurifier
27 $config = HTMLPurifier_Config::createDefault();
28 foreach ($options as $option) {
29 $config->set($option[0], $option[1]);
30 }
31
32 // Creating a HTMLPurifier with it’s config
33 $purifier = new HTMLPurifier($config);
34
35 // Fetch the RSS
36 try {
37 $rss = ZendFeedReaderReader::import(’http://www.planet-php.net/rss/’);
38 } catch (ZendFeedExceptionReaderRuntimeException $e) {
39 // feed import failed
40 echo "Exception caught importing feed: {$e->getMessage()}n";
41 exit;
42 }
43
44 // Initialize the channel data array
45 // See that we’re cleaning the description with HTMLPurifier
46 $channel = array(
47 ’title’ => $rss->getTitle(),
48 ’link’ => $rss->getLink(),
49 ’description’ => $purifier->purify($rss->getDescription()),
50 ’items’ => array()
51 );
52
53 // Loop over each channel item and store relevant data
54 // See that we’re cleaning the descriptions with HTMLPurifier
55 foreach ($rss as $item) {
56 $channel[’items’][] = array(
57 ’title’ => $item->getTitle(),
58 ’link’ => $item->getLink(),
59 ’description’ => $purifier->purify($item->getDescription())
60 );
61 }
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.
462 Chapter 115. ZendFeed and Security
Zend Framework 2 Documentation, Release 2.3.1dev
115.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 try {
2 $rss = ZendFeedReaderReader::import(’http://www.planet-php.net/rss/’);
3 } catch (ZendFeedExceptionReaderRuntimeException $e) {
4 // feed import failed
5 echo "Exception caught importing feed: {$e->getMessage()}n";
6 exit;
7 }
8
9 // Validate all URIs
10 $linkValidator = new ZendValidatorUri;
11 $link = null;
12 if ($linkValidator->isValid($rss->getLink())) {
13 $link = $rss->getLink();
14 }
15
16 // Escaper used for escaping data
17 $escaper = new ZendEscaperEscaper(’utf-8’);
18
19 // Initialize the channel data array
20 $channel = array(
21 ’title’ => $escaper->escapeHtml($rss->getTitle()),
22 ’link’ => $escaper->escapeHtml($link),
23 ’description’ => $escaper->escapeHtml($rss->getDescription()),
24 ’items’ => array()
25 );
26
27 // Loop over each channel item and store relevant data
28 foreach ($rss as $item) {
29 $link = null;
30 if ($linkValidator->isValid($rss->getLink())) {
31 $link = $item->getLink();
32 }
33 $channel[’items’][] = array(
34 ’title’ => $escaper->escapeHtml($item->getTitle()),
35 ’link’ => $escaper->escapeHtml($link),
36 ’description’ => $escaper->escapeHtml($item->getDescription())
37 );
38 }
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.
115.3. Escaping data using ZendEscaper 463
Zend Framework 2 Documentation, Release 2.3.1dev
464 Chapter 115. ZendFeed and Security
CHAPTER 116
ZendFeedReaderReader
116.1 Introduction
ZendFeedReaderReader is a component used to consume RSS and Atom feeds of any version, includ-
ing 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.
116.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.
1 $feed = ZendFeedReaderReader::import(’http://www.planet-php.net/rdf/’);
2 $data = array(
3 ’title’ => $feed->getTitle(),
465
Zend Framework 2 Documentation, Release 2.3.1dev
4 ’link’ => $feed->getLink(),
5 ’dateModified’ => $feed->getDateModified(),
6 ’description’ => $feed->getDescription(),
7 ’language’ => $feed->getLanguage(),
8 ’entries’ => array(),
9 );
10
11 foreach ($feed as $entry) {
12 $edata = array(
13 ’title’ => $entry->getTitle(),
14 ’description’ => $entry->getDescription(),
15 ’dateModified’ => $entry->getDateModified(),
16 ’authors’ => $entry->getAuthors(),
17 ’link’ => $entry->getLink(),
18 ’content’ => $entry->getContent()
19 );
20 $data[’entries’][] = $edata;
21 }
The example above demonstrates ZendFeedReaderReader‘s API, and it also demonstrates some of its inter-
nal 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 // from a URI
2 $feed = ZendFeedReaderReader::import(’http://www.planet-php.net/rdf/’);
3
4 // from a String
5 $feed = ZendFeedReaderReader::importString($feedXmlString);
6
7 // from a file
8 $feed = ZendFeedReaderReader::importFile(’./feed.xml’);
116.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).
• 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.
466 Chapter 116. ZendFeedReaderReader
Zend Framework 2 Documentation, Release 2.3.1dev
• 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 $feed = ZendFeedReaderReader::import(’http://www.planet-php.net/rdf/’);
2 $xpathPrefix = $feed->getXpathPrefix();
3 $xpath = $feed->getXpath();
4 $xpath->registerNamespace(’admin’, ’http://webns.net/mvcb/’);
5 $reportErrorsTo = $xpath->evaluate(’string(’
6 . $xpathPrefix
7 . ’/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.
116.4 Cache Support and Intelligent Requests
116.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);
116.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);
4 ZendFeedReaderReader::useHttpConditionalGet();
5
6 $feed = ZendFeedReaderReader::import(’http://www.planet-php.net/rdf/’);
116.4. Cache Support and Intelligent Requests 467
Zend Framework 2 Documentation, Release 2.3.1dev
In the example above, with HTTP Conditional GET requests enabled, the response header values for ETag and Last-
Modified 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 $lastEtagReceived = ’5e6cefe7df5a7e95c8b1ba1a2ccaff3d’;
2 $lastModifiedDateReceived = ’Wed, 08 Jul 2009 13:37:22 GMT’;
3 $feed = ZendFeedReaderReader::import(
4 $uri, $lastEtagReceived, $lastModifiedDateReceived
5 );
116.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 if (isset($links->rdf)) {
4 echo $links->rdf, "n"; // http://www.planet-php.org/rdf/
5 }
6 if (isset($links->rss)) {
7 echo $links->rss, "n"; // http://www.planet-php.org/rss/
8 }
9 if (isset($links->atom)) {
10 echo $links->atom, "n"; // http://www.planet-php.org/atom/
11 }
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 foreach ($links as $link) {
4 echo $link[’href’], "n";
5 }
468 Chapter 116. ZendFeedReaderReader
Zend Framework 2 Documentation, Release 2.3.1dev
116.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 $feed = ZendFeedReaderReader::import(’http://www.example.com/atom.xml’);
2 $categories = $feed->getCategories();
3 $labels = array();
4 foreach ($categories as $cat) {
5 $labels[] = $cat[’label’]
6 }
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 $feed = ZendFeedReaderReader::import(’http://www.example.com/atom.xml’);
2 $categories = $feed->getCategories();
3 $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.
116.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
116.6. Attribute Collections 469
Zend Framework 2 Documentation, Release 2.3.1dev
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.
470 Chapter 116. ZendFeedReaderReader
Zend Framework 2 Documentation, Release 2.3.1dev
Table 116.1: Feed Level API Methods
getId() Returns a unique ID associated with this feed
getTitle() Returns the title of the feed
getDescription() Returns the text description of the feed.
getLink() 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).
getFeedLink() 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.
getAuthors() 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.
getAu-
thor(integer
$index = 0)
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).
getDateCre-
ated()
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.
getDateModi-
fied()
Returns the date on which this feed was last modified. The returned date will be a DateTime
object.
getLastBuild-
Date()
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.
getLanguage() Returns the language of the feed (if defined) or simply the language noted in the XML
document.
getGenerator() 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.
getCopyright() Returns any copyright notice associated with the feed.
getHubs() 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.
getCategories() 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.
getImage() 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 ele-
ments 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 re-
cent, 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.
116.7. Retrieving Feed Information 471
Zend Framework 2 Documentation, Release 2.3.1dev
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 116.2: Extended Feed Level API Methods
getDomDocu-
ment()
Returns the parent DOMDocument object for the entire source XML document
getElement() Returns the current feed level DOMElement object
saveXml() Returns a string containing an XML document of the entire feed element (this is not the
original document but a rebuilt version)
getXpath() Returns the DOMXPath object used internally to run queries on the DOMDocument object
(this includes core and Extension namespaces pre-registered)
getXpathPre-
fix()
Returns the valid DOM path prefix prepended to all XPath queries matching the feed being
queried
getEncoding() 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.
count() Returns a count of the entries or items this feed contains (implements SPLCountable interface)
current() Returns either the current entry (using the current index from key())
key() Returns the current entry index
next() Increments the entry index value by one
rewind() Resets the entry index to 0
valid() Checks that the current entry index is valid, i.e. it does fall below 0 and does not exceed the
number of entries existing.
getExten-
sions()
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.
getExten-
sion(string
$name)
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.
getType() 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.
116.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.
472 Chapter 116. ZendFeedReaderReader
Zend Framework 2 Documentation, Release 2.3.1dev
Table 116.3: Entry Level API Methods
getId() Returns a unique ID for the current entry.
getTitle() Returns the title of the current entry.
getDescription() Returns a description of the current entry.
getLink() Returns a URI to the HTML version of the current entry.
getPermaLink() Returns the permanent link to the current entry. In most cases, this is the same as
using getLink().
getAuthors() 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.
getAuthor(integer $index
= 0)
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).
getDateCreated() 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.
getDateModified() Returns the date on which the current entry was last modified
getContent() 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.
getEnclosure() 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.
getCommentCount() Returns the number of comments made on this entry at the time the feed was last
generated
getCommentLink() Returns a URI pointing to the HTML page where comments can be made on this
entry
getComment-
FeedLink([string $type =
‘atom’|’rss’])
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).
getCategories() 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.
116.8. Retrieving Entry/Item Information 473
Zend Framework 2 Documentation, Release 2.3.1dev
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 116.4: Extended Entry Level API Methods
getDomDocu-
ment()
Returns the parent DOMDocument object for the entire feed (not just the current entry)
getElement() Returns the current entry level DOMElement object
getXpath() Returns the DOMXPath object used internally to run queries on the DOMDocument object
(this includes core and Extension namespaces pre-registered)
getXpathPre-
fix()
Returns the valid DOM path prefix prepended to all XPath queries matching the entry being
queried
getEncoding() 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.
getExten-
sions()
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.
getExten-
sion(string
$name)
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.
getType() 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.
116.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-
dard’s name. For example, internally we have ZendFeedReaderExtensionDublinCoreFeed and
474 Chapter 116. ZendFeedReaderReader
Zend Framework 2 Documentation, Release 2.3.1dev
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 116.5: Core Extensions (pre-registered)
DublinCore (Feed and Entry) Implements support for Dublin Core Metadata Element Set 1.0 and 1.1
Content (Entry only) Implements support for Content 1.0
Atom (Feed and Entry) Implements support for Atom 0.3 and Atom 1.0
Slash Implements support for the Slash RSS 1.0 module
WellFormedWeb Implements support for the Well Formed Web CommentAPI 1.0
Thread Implements support for Atom Threading Extensions as described in RFC 4685
Podcast 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 116.6: Non-Core Extensions (must register manually)
Syndication Implements Syndication 1.0 support for RSS feeds
CreativeCom-
mons
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 ZendFeedReaderReader::registerExtension(’Syndication’);
2 $feed = ZendFeedReaderReader::import(’http://rss.slashdot.org/Slashdot/slashdot’);
3 $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 ZendFeedReaderReader::registerExtension(’Syndication’);
2 $feed = ZendFeedReaderReader::import(’http://rss.slashdot.org/Slashdot/slashdot’);
3 $syndication = $feed->getExtension(’Syndication’);
4 $updatePeriod = $syndication->getUpdatePeriod();
116.9. Extending Feed and Entry APIs 475
Zend Framework 2 Documentation, Release 2.3.1dev
116.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 JungleBooks 1.0:
2 http://example.com/junglebooks/rss/module/1.0/
A snippet of RSS containing this extension in practice could be something similar to:
1 <?xml version="1.0" encoding="utf-8" ?>
2 <rss version="2.0"
3 xmlns:content="http://purl.org/rss/1.0/modules/content/"
4 xmlns:jungle="http://example.com/junglebooks/rss/module/1.0/">
5 <channel>
6 <title>Jungle Books Customer Reviews</title>
7 <link>http://example.com/junglebooks</link>
8 <description>Many book reviews!</description>
9 <pubDate>Fri, 26 Jun 2009 19:15:10 GMT</pubDate>
10 <jungle:dayPopular>
11 http://example.com/junglebooks/book/938
12 </jungle:dayPopular>
13 <item>
14 <title>Review Of Flatland: A Romance of Many Dimensions</title>
15 <link>http://example.com/junglebooks/review/987</link>
16 <author>Confused Physics Student</author>
17 <content:encoded>
18 A romantic square?!
19 </content:encoded>
20 <pubDate>Thu, 25 Jun 2009 20:03:28 -0700</pubDate>
21 <jungle:isbn>048627263X</jungle:isbn>
22 </item>
23 </channel>
24 </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 class MyFeedReaderExtensionJungleBooksEntry
2 extends ZendFeedReaderExtensionAbstractEntry
3 {
4 public function getIsbn()
5 {
6 if (isset($this->data[’isbn’])) {
7 return $this->data[’isbn’];
8 }
9 $isbn = $this->xpath->evaluate(
10 ’string(’ . $this->getXpathPrefix() . ’/jungle:isbn)’
11 );
12 if (!$isbn) {
13 $isbn = null;
476 Chapter 116. ZendFeedReaderReader
Zend Framework 2 Documentation, Release 2.3.1dev
14 }
15 $this->data[’isbn’] = $isbn;
16 return $this->data[’isbn’];
17 }
18
19 protected function registerNamespaces()
20 {
21 $this->xpath->registerNamespace(
22 ’jungle’, ’http://example.com/junglebooks/rss/module/1.0/’
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 if (!ZendFeedReaderReader::isRegistered(’JungleBooks’)) {
2 $extensions = ZendFeedReaderReader::getExtensionManager();
3 $extensions->setInvokableClass(’JungleBooksEntry’, ’MyFeedReaderExtensionJungleBooksEntry’);
4 ZendFeedReaderReader::registerExtension(’JungleBooks’);
5 }
6 $feed = ZendFeedReaderReader::import(’http://example.com/junglebooks/rss’);
7
8 // ISBN for whatever book the first entry in the feed was concerned with
9 $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 class MyFeedReaderExtensionJungleBooksFeed
2 extends ZendFeedReaderExtensionAbstractFeed
3 {
4 public function getDaysPopularBookLink()
5 {
6 if (isset($this->data[’dayPopular’])) {
7 return $this->data[’dayPopular’];
8 }
9 $dayPopular = $this->xpath->evaluate(
10 ’string(’ . $this->getXpathPrefix() . ’/jungle:dayPopular)’
11 );
12 if (!$dayPopular) {
13 $dayPopular = null;
14 }
15 $this->data[’dayPopular’] = $dayPopular;
16 return $this->data[’dayPopular’];
17 }
18
116.9. Extending Feed and Entry APIs 477
Zend Framework 2 Documentation, Release 2.3.1dev
19 protected function registerNamespaces()
20 {
21 $this->xpath->registerNamespace(
22 ’jungle’, ’http://example.com/junglebooks/rss/module/1.0/’
23 );
24 }
25 }
Let’s repeat the last example using a custom Extension to show the method being used.
1 if (!ZendFeedReaderReader::isRegistered(’JungleBooks’)) {
2 $extensions = ZendFeedReaderReader::getExtensionManager();
3 $extensions->setInvokableClass(’JungleBooksFeed’, ’MyFeedReaderExtensionJungleBooksFeed’);
4 ZendFeedReaderReader::registerExtension(’JungleBooks’);
5 }
6 $feed = ZendFeedReaderReader::import(’http://example.com/junglebooks/rss’);
7
8 // URI to the information page of the day’s most popular book with visitors
9 $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.
478 Chapter 116. ZendFeedReaderReader
CHAPTER 117
ZendFeedWriterWriter
117.1 Introduction
ZendFeedWriterWriter is the sibling component to ZendFeedReaderReader responsible for gen-
erating 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.
117.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
the entry’s original source). The latter implements the Atom Tombstones RFC allowing feeds to carry references to
entries which have been deleted.
479
Zend Framework 2 Documentation, Release 2.3.1dev
While there are two main data container types, there are four renderers - two matching container renderers per sup-
ported 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.
117.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 * Create the parent feed
3 */
4 $feed = new ZendFeedWriterFeed;
5 $feed->setTitle(’Paddy’s Blog’);
6 $feed->setLink(’http://www.example.com’);
7 $feed->setFeedLink(’http://www.example.com/atom’, ’atom’);
8 $feed->addAuthor(array(
9 ’name’ => ’Paddy’,
10 ’email’ => ’paddy@example.com’,
11 ’uri’ => ’http://www.example.com’,
12 ));
13 $feed->setDateModified(time());
14 $feed->addHub(’http://pubsubhubbub.appspot.com/’);
15
16 /**
17 * Add one or more entries. Note that entries must
18 * be manually added once created.
19 */
20 $entry = $feed->createEntry();
21 $entry->setTitle(’All Your Base Are Belong To Us’);
22 $entry->setLink(’http://www.example.com/all-your-base-are-belong-to-us’);
23 $entry->addAuthor(array(
24 ’name’ => ’Paddy’,
25 ’email’ => ’paddy@example.com’,
26 ’uri’ => ’http://www.example.com’,
27 ));
28 $entry->setDateModified(time());
29 $entry->setDateCreated(time());
30 $entry->setDescription(’Exposing the difficultly of porting games to English.’);
31 $entry->setContent(
32 ’I am not writing the article. The example is long enough as is ;).’
33 );
480 Chapter 117. ZendFeedWriterWriter
Zend Framework 2 Documentation, Release 2.3.1dev
34 $feed->addEntry($entry);
35
36 /**
37 * Render the resulting feed to Atom 1.0 and assign to $out.
38 * You can substitute "atom" with "rss" to generate an RSS 2.0 feed.
39 */
40 $out = $feed->export(’atom’);
The output rendered should be as follows:
1 <?xml version="1.0" encoding="utf-8"?>
2 <feed xmlns="http://www.w3.org/2005/Atom">
3 <title type="text">Paddy’s Blog</title>
4 <subtitle type="text">Writing about PC Games since 176 BC.</subtitle>
5 <updated>2009-12-14T20:28:18+00:00</updated>
6 <generator uri="http://framework.zend.com" version="1.10.0alpha">
7 ZendFeedWriter
8 </generator>
9 <link rel="alternate" type="text/html" href="http://www.example.com"/>
10 <link rel="self" type="application/atom+xml"
11 href="http://www.example.com/atom"/>
12 <id>http://www.example.com</id>
13 <author>
14 <name>Paddy</name>
15 <email>paddy@example.com</email>
16 <uri>http://www.example.com</uri>
17 </author>
18 <link rel="hub" href="http://pubsubhubbub.appspot.com/"/>
19 <entry>
20 <title type="html"><![CDATA[All Your Base Are Belong To
21 Us]]></title>
22 <summary type="html">
23 <![CDATA[Exposing the difficultly of porting games to
24 English.]]>
25 </summary>
26 <published>2009-12-14T20:28:18+00:00</published>
27 <updated>2009-12-14T20:28:18+00:00</updated>
28 <link rel="alternate" type="text/html"
29 href="http://www.example.com/all-your-base-are-belong-to-us"/>
30 <id>http://www.example.com/all-your-base-are-belong-to-us</id>
31 <author>
32 <name>Paddy</name>
33 <email>paddy@example.com</email>
34 <uri>http://www.example.com</uri>
35 </author>
36 <content type="html">
37 <![CDATA[I am not writing the article.
38 The example is long enough as is ;).]]>
39 </content>
40 </entry>
41 </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
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.
117.3. Getting Started 481
Zend Framework 2 Documentation, Release 2.3.1dev
117.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.
482 Chapter 117. ZendFeedWriterWriter
Zend Framework 2 Documentation, Release 2.3.1dev
Table 117.1: Feed Level API Methods
setId() 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.
setTitle() Set the title of the feed.
setDe-
scription()
Set the text description of the feed.
setLink() 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).
set-
FeedLinks()
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”.
addAu-
thors()
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.
addAu-
thor()
Sets the data for a single author following the same array format as described above for a single
sub-array.
setDate-
Created()
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.
setDate-
Modified()
Sets the date on which this feed was last modified. The expected parameter may be a UNIX
timestamp or a DateTime object.
setLast-
Build-
Date()
Sets the date on which this feed was last build. The expected parameter may be a UNIX timestamp
or a DateTime object. This will only be rendered for RSS 2.0 feeds and is automatically rendered as
the current date by default when not explicitly set.
set-
Language()
Sets the language of the feed. This will be omitted unless set.
setGenera-
tor()
Allows the setting of a generator. The parameter should be an array containing the keys “name”,
“version” and “uri”. If omitted a default generator will be added referencing ZendFeedWriter, the
current Zend Framework version and the Framework’s URI.
setCopy-
right()
Sets a copyright notice associated with the feed.
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.
addCate-
gories()
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 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.
createEn-
try()
Returns a new instance of ZendFeedWriterEntry. This is the Entry level data container. New entries
are not automatically assigned to the current feed, so you must explicitly call addEntry() to add the
entry for rendering.
addEntry() Adds an instance of ZendFeedWriterEntry to the current feed container for rendering.
create-
Tomb-
stone()
Returns a new instance of ZendFeedWriterDeleted. This is the Atom 2.0 Tombstone level data
container. New entries are not automatically assigned to the current feed, so you must explicitly call
addTombstone() to add the deleted entry for rendering.
117.4. Setting Feed Data Points 483
Zend Framework 2 Documentation, Release 2.3.1dev
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.
117.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.
484 Chapter 117. ZendFeedWriterWriter
Zend Framework 2 Documentation, Release 2.3.1dev
Table 117.2: Entry Level API Methods
setId() 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.
setTitle() Set the title of the entry.
setDescrip-
tion()
Set the text description of the entry.
setContent() Set the content of the entry.
setLink() 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).
set-
FeedLinks()
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.
addAuthors() 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.
addAuthor() Sets the data for a single author following the same format as described above for a single
sub-array.
setDateCre-
ated()
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.
setDateModi-
fied()
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.
setCopy-
right()
Sets a copyright notice associated with the feed.
setCate-
gories()
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.
setComment-
Count()
Sets the number of comments associated with this entry. Rendering differs between RSS and
Atom 2.0 depending on the element or attribute needed.
setCom-
mentLink()
Seta a link to a HTML page containing comments associated with this entry.
setComment-
FeedLink()
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”.
setComment-
FeedLinks()
Same as setCommentFeedLink() except it accepts an array of arrays, where each subarray
contains the expected parameters of setCommentFeedLink().
setEncoding() 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.
117.5. Setting Entry Data Points 485
Zend Framework 2 Documentation, Release 2.3.1dev
486 Chapter 117. ZendFeedWriterWriter
CHAPTER 118
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.
118.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, Feed-
burner, and there are plugins available for Wordpress blogs.
118.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.
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
487
Zend Framework 2 Documentation, Release 2.3.1dev
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 communi-
cation 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 immedi-
ately 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 addi-
tion, 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.
118.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 $publisher = new ZendFeedPubSubHubbubPublisher;
2 $publisher->addHubUrls(array(
3 ’http://pubsubhubbub.appspot.com/’,
4 ’http://hubbub.example.com’,
5 ));
6 $publisher->addUpdatedTopicUrls(array(
7 ’http://www.example.net/rss’,
8 ’http://www.example.net/atom’,
9 ));
10 $publisher->notifyAll();
11
12 if (!$publisher->isSuccess()) {
13 // check for errors
14 $errors = $publisher->getErrors();
488 Chapter 118. ZendFeedPubSubHubbub
Zend Framework 2 Documentation, Release 2.3.1dev
15 $failedHubs = array();
16 foreach ($errors as $error) {
17 $failedHubs[] = $error[’hubUrl’];
18 }
19 }
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.
118.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.
118.4.1 Subscribing and Unsubscribing
ZendFeedPubSubHubbubSubscriber implements a full Pubsubhubbub Subscriber capable of subscrib-
ing to, or unsubscribing from, any Topic via any Hub advertised by that Topic. It operates in conjunction with
118.4. ZendFeedPubSubHubbubSubscriber 489
Zend Framework 2 Documentation, Release 2.3.1dev
ZendFeedPubSubHubbubSubscriberCallback which accepts requests from a Hub to confirm all sub-
scription 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 $subscriber = new ZendFeedPubSubHubbubSubscriber;
4 $subscriber->setStorage($storage);
5 $subscriber->addHubUrl(’http://hubbub.example.com’);
6 $subscriber->setTopicUrl(’http://www.example.net/rss.xml’);
7 $subscriber->setCallbackUrl(’http://www.mydomain.com/hubbub/callback’);
8 $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 ap-
plication. 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 CREATE TABLE IF NOT EXISTS ‘subscription‘ (
2 ‘id‘ varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT ’’,
3 ‘topic_url‘ varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
4 ‘hub_url‘ varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
5 ‘created_time‘ datetime DEFAULT NULL,
6 ‘lease_seconds‘ bigint(20) DEFAULT NULL,
7 ‘verify_token‘ varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
8 ‘secret‘ varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
9 ‘expiration_time‘ datetime DEFAULT NULL,
10 ‘subscription_state‘ varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL,
11 PRIMARY KEY (‘id‘)
12 ) 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):
490 Chapter 118. ZendFeedPubSubHubbub
Zend Framework 2 Documentation, Release 2.3.1dev
Table 118.1: Subscription request parameters
Pa-
ram-
eter
Value Explanation
hub.callbackhttp://www.mydomain.com/hubbub/callback?xhub.subscription=5536df06b5dcb966edab3a4c4dThe 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 ZendPubSubHubbubSubscriberCall-
back::setSubscriptionKey(). A detailed example is
offered later.
hub.lease_seconds2592000 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.modesubscribe 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.verifysync 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.verifyasync 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_token3065919804ab-
caa7212ae89.879827871253878386
A verification token returned to the Subscriber by the
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.
118.4. ZendFeedPubSubHubbubSubscriber 491
Zend Framework 2 Documentation, Release 2.3.1dev
You can modify several of these parameters to indicate a different prefer-
ence. For example, you can set a different lease seconds value using
ZendFeedPubSubHubbubSubscriber::setLeaseSeconds() or show a preference for the async ver-
ify mode by using setPreferredVerificationMode(ZendFeedPubSubHubbubPubSubHubbub::VERIFICATION_
However the Hubs retain the capability to enforce their own preferences and for this reason the component is deliber-
ately 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 sup-
ported by ZendFeedPubSubHubbub), you may indicate a specific preference using the
setPreferredVerificationMode() method. In “sync” (synchronous) mode, the Hub attempts to con-
firm 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.
118.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 $storage = new ZendFeedPubSubHubbubModelSubscription;
2 $callback = new ZendFeedPubSubHubbubSubscriberCallback;
3 $callback->setStorage($storage);
4 $callback->handle();
5 $callback->sendResponse();
6
7 /**
8 * Check if the callback resulting in the receipt of a feed update.
9 * Otherwise it was either a (un)sub verification request or invalid request.
10 * Typically we need do nothing other than add feed update handling - the rest
11 * is handled internally by the class.
12 */
13 if ($callback->hasFeedUpdate()) {
14 $feedString = $callback->getFeedUpdate();
15 /**
16 * Process the feed update asynchronously to avoid a Hub timeout.
492 Chapter 118. ZendFeedPubSubHubbub
Zend Framework 2 Documentation, Release 2.3.1dev
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.
118.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 specifi-
cation, 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 param-
eter being the key value available from the Router. The example below demonstrates this using a Zend Framework
controller.
1 use ZendMvcControllerAbstractActionController;
2
3 class CallbackController extends AbstractActionController
4 {
5
6 public function indexAction()
7 {
8 $storage = new ZendFeedPubSubHubbubModelSubscription;
9 $callback = new ZendFeedPubSubHubbubSubscriberCallback;
10 $callback->setStorage($storage);
11 /**
12 * Inject subscription key parsing from URL path using
13 * a parameter from Router.
14 */
118.4. ZendFeedPubSubHubbubSubscriber 493
Zend Framework 2 Documentation, Release 2.3.1dev
15 $subscriptionKey = $this->params()->fromRoute(’subkey’);
16 $callback->setSubscriptionKey($subscriptionKey);
17 $callback->handle();
18 $callback->sendResponse();
19
20 /**
21 * Check if the callback resulting in the receipt of a feed update.
22 * Otherwise it was either a (un)sub verification request or invalid
23 * request. Typically we need do nothing other than add feed update
24 * handling - the rest is handled internally by the class.
25 */
26 if ($callback->hasFeedUpdate()) {
27 $feedString = $callback->getFeedUpdate();
28 /**
29 * Process the feed update asynchronously to avoid a Hub timeout.
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 // Callback Route to enable appending a PuSH Subscription’s lookup key
2 $route = ZendMvcRouterHttpSegment::factory(array(
3 ’route’ => ’/callback/:subkey’,
4 ’constraints’ => array(
5 ’subkey’ => ’[a-z0-9]+’
6 ),
7 ’defaults’ => array(
8 ’controller’ => ’application-index’,
9 ’action’ => ’index’
10 )
11 ));
494 Chapter 118. ZendFeedPubSubHubbub
CHAPTER 119
ZendFileClassFileLocator
119.1 Overview
TODO
119.2 Available Methods
TODO
119.3 Examples
TODO
495
Zend Framework 2 Documentation, Release 2.3.1dev
496 Chapter 119. ZendFileClassFileLocator
CHAPTER 120
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.
120.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 trans-
formation 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 con-
tain 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.
120.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 echo $htmlEntities->filter(’&’); // &amp;
4 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:
497
Zend Framework 2 Documentation, Release 2.3.1dev
1 $strtolower = new ZendFilterStringToLower;
2
3 echo $strtolower(’I LOVE ZF2!’); // i love zf2!
4 $zf2love = $strtolower(’I LOVE ZF2!’);
orphan
120.3 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 echo StaticFilter::execute(’"’,
2 ’HtmlEntities’,
3 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 $pluginManager = StaticFilter::getPluginManager()->setInvokableClass(
2 ’myNewFilter’, ’MyCustomFilterMyNewFilter’
3 );
4
5 StaticFilter::setPluginManager(new MyFilterPluginManager());
This is useful when adding custom filters to be used by the StaticFilter.
120.4 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
3 // Attach a filter
4 $filter = new ZendFilterWordUnderscoreToCamelCase();
5 $filtered = $filter->filter($original);
6
7 // Use it’s opposite
8 $filter2 = new ZendFilterWordCamelCaseToUnderscore();
9 $filtered = $filter2->filter($filtered)
498 Chapter 120. Introduction to ZendFilter
Zend Framework 2 Documentation, Release 2.3.1dev
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.
120.4. Double filtering 499
Zend Framework 2 Documentation, Release 2.3.1dev
500 Chapter 120. Introduction to ZendFilter
CHAPTER 121
Standard Filter Classes
Zend Framework comes with a standard set of filters, which are ready for you to use.
orphan
121.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.
121.1.1 Supported Options
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()
121.1.2 Basic Usage
1 // Default settings, deny whitespace
2 $filter = new ZendI18nFilterAlnum();
3 echo $filter->filter("This is (my) content: 123");
4 // Returns "Thisismycontent123"
5
6 // First param in constructor is $allowWhiteSpace
7 $filter = new ZendI18nFilterAlnum(true);
8 echo $filter->filter("This is (my) content: 123");
9 // Returns "This is my content 123"
501
Zend Framework 2 Documentation, Release 2.3.1dev
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.
orphan
121.2 Alpha
The Alpha filter can be used to return only alphabetic characters in the unicode “letter” category. All other characters
are suppressed.
121.2.1 Supported Options
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()
121.2.2 Basic Usage
1 // Default settings, deny whitespace
2 $filter = new ZendI18nFilterAlpha();
3 echo $filter->filter("This is (my) content: 123");
4 // Returns "Thisismycontent"
5
6 // Allow whitespace
7 $filter = new ZendI18nFilterAlpha(true);
8 echo $filter->filter("This is (my) content: 123");
9 // 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.
orphan
121.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.
502 Chapter 121. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.3.1dev
121.3.1 Supported Options
There are no additional options for ZendFilterBaseName.
121.3.2 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‘.
orphan
121.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.
121.4.1 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.
121.4.2 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 $filter = new ZendFilterBoolean();
2 $value = ’’;
3 $result = $filter->filter($value);
4 // 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.
121.4. Boolean 503
Zend Framework 2 Documentation, Release 2.3.1dev
121.4.3 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 // converts 0 to false
2 $filter = new ZendFilterBoolean(ZendFilterBoolean::INTEGER);
3
4 // converts 0 and ’0’ to false
5 $filter = new ZendFilterBoolean(
6 ZendFilterBoolean::INTEGER + ZendFilterBoolean::ZERO
7 );
8
9 // converts 0 and ’0’ to false
10 $filter = new ZendFilterBoolean(array(
11 ’type’ => array(
12 ZendFilterBoolean::INTEGER,
13 ZendFilterBoolean::ZERO,
14 ),
15 ));
16
17 // converts 0 and ’0’ to false
18 $filter = new ZendFilterBoolean(array(
19 ’type’ => array(
20 ’integer’,
21 ’zero’,
22 ),
23 ));
You can also give an instance of ZendConfigConfig to set the desired types. To set types after instantiation,
use the setType() method.
504 Chapter 121. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.3.1dev
121.4.4 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 $filter = new ZendFilterBoolean(array(
2 ’type’ => ZendFilterBoolean::ALL,
3 ’locale’ => ’de’,
4 ));
5
6 // returns false
7 echo $filter->filter(’nein’);
8
9 $filter->setLocale(’en’);
10
11 // returns true
12 $filter->filter(’yes’);
121.4.5 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 121.1: Usage without casting
Type True False
ZendFilterBoolean::BOOLEAN TRUE FALSE
ZendFilterBoolean::INTEGER 0 1
ZendFilterBoolean::FLOAT 0.0 1.0
ZendFilterBoolean::STRING “”
ZendFilterBoolean::ZERO “0” “1”
ZendFilterBoolean::EMPTY_ARRAY array()
ZendFilterBoolean::NULL NULL
ZendFilterBoolean::FALSE_STRING “false” (case independently) “true” (case independently)
ZendFilterBoolean::YES localized “yes” (case independently) localized “no” (case independently)
The following example shows the behaviour when changing the casting option:
1 $filter = new ZendFilterBoolean(array(
2 ’type’ => ZendFilterBoolean::ALL,
3 ’casting’ => false,
4 ));
5
6 // returns false
7 echo $filter->filter(0);
8
9 // returns true
10 echo $filter->filter(1);
11
12 // returns the value
13 echo $filter->filter(2);
121.4. Boolean 505
Zend Framework 2 Documentation, Release 2.3.1dev
orphan
121.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.
121.5.1 Supported Options
The following options are supported for ZendFilterCallback:
• callback: This sets the callback which should be used.
• callback_params: This property sets the options which are used when the callback is processed.
121.5.2 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 print $filter->filter(’Hello!’);
4 // 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 // Our classdefinition
2 class MyClass
3 {
4 public function Reverse($param);
5 }
6
7 // The filter definition
8 $filter = new ZendFilterCallback(array(’MyClass’, ’Reverse’));
9 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.
121.5.3 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 $filter = new ZendFilterCallback(
2 array(
3 ’callback’ => ’MyMethod’,
4 ’options’ => array(’key’ => ’param1’, ’key2’ => ’param2’)
5 )
506 Chapter 121. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.3.1dev
6 );
7 $filter->filter(array(’value’ => ’Hello’));
When you would call the above method definition manually it would look like this:
1 $value = MyMethod(’Hello’, ’param1’, ’param2’);
orphan
121.6 Compress and Decompress
These two filters are capable of compressing and decompressing strings, files, and directories.
121.6.1 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.
121.6.2 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 ap-
proximately 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)
121.6.3 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’);
121.6. Compress and Decompress 507
Zend Framework 2 Documentation, Release 2.3.1dev
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).
1 $filter = new ZendFilterCompress(array(
2 ’adapter’ => ’Bz2’,
3 ’options’ => array(
4 ’blocksize’ => 8,
5 ),
6 ));
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 $filter = new ZendFilterCompress(’Bz2’);
2 $compressed = $filter->filter(’Uncompressed string’);
3 // Returns the compressed string
Decompression works the same way.
1 $filter = new ZendFilterDecompress(’Bz2’);
2 $compressed = $filter->filter(’Compressed string’);
3 // 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.
121.6.4 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 $filter = new ZendFilterCompress(array(
2 ’adapter’ => ’Bz2’,
3 ’options’ => array(
4 ’archive’ => ’filename.bz2’,
5 ),
6 ));
7 $compressed = $filter->filter(’Uncompressed string’);
8 // 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.
508 Chapter 121. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.3.1dev
When you want to compress a file, then you must give the name of the file with its path.
1 $filter = new ZendFilterCompress(array(
2 ’adapter’ => ’Bz2’,
3 ’options’ => array(
4 ’archive’ => ’filename.bz2’
5 ),
6 ));
7 $compressed = $filter->filter(’C:tempcompressme.txt’);
8 // 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 subdirec-
tories will be compressed into the archive.
1 $filter = new ZendFilterCompress(array(
2 ’adapter’ => ’Bz2’,
3 ’options’ => array(
4 ’archive’ => ’filename.bz2’
5 ),
6 ));
7 $compressed = $filter->filter(’C:tempsomedir’);
8 // 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.
121.6.5 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 $filter = new ZendFilterDecompress(’Bz2’);
2 $decompressed = $filter->filter(’filename.bz2’);
3 // 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 $filter = new ZendFilterDecompress(array(
2 ’adapter’ => ’Zip’,
3 ’options’ => array(
4 ’target’ => ’C:temp’,
5 )
6 ));
7 $decompressed = $filter->filter(’filename.zip’);
8 // Returns true on success and decompresses the archive file
9 // 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.
121.6. Compress and Decompress 509
Zend Framework 2 Documentation, Release 2.3.1dev
121.6.6 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.
121.6.7 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.
121.6.8 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.
510 Chapter 121. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.3.1dev
121.6.9 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.
121.6.10 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.
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.
121.6. Compress and Decompress 511
Zend Framework 2 Documentation, Release 2.3.1dev
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.
121.6.11 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.
orphan
121.7 Digits
Returns the string $value, removing all but digits.
121.7.1 Supported Options
There are no additional options for ZendFilterDigits.
121.7.2 Basic Usage
A basic example of usage is below:
1 $filter = new ZendFilterDigits();
2
3 print $filter->filter(’October 2012’);
This returns “2012”.
1 $filter = new ZendFilterDigits();
2
3 print $filter->filter(’HTML 5 for Dummies’);
512 Chapter 121. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.3.1dev
This returns “5”.
orphan
121.8 Dir
Given a string containing a path to a file, this function will return the name of the directory.
121.8.1 Supported Options
There are no additional options for ZendFilterDir.
121.8.2 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”.
orphan
121.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.
121.9.1 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.
121.8. Dir 513
Zend Framework 2 Documentation, Release 2.3.1dev
• 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.
121.9.2 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 // Use the BlockCipher adapter
2 $filter1 = new ZendFilterEncrypt(array(’adapter’ => ’BlockCipher’));
3
4 // Use the OpenSSL adapter
5 $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 // Use the OpenSSL adapter
2 $filter = new ZendFilterEncrypt();
3 $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.
121.9.3 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 // Use the default AES encryption algorithm
2 $filter = new ZendFilterEncrypt(array(’adapter’ => ’BlockCipher’));
3 $filter->setKey(’encryption key’);
4
5 // or
6 // $filter = new ZendFilterEncrypt(array(
7 // ’adapter’ => ’BlockCipher’,
8 // ’key’ => ’encryption key’
9 // ));
10
514 Chapter 121. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.3.1dev
11 $encrypted = $filter->filter(’text to be encrypted’);
12 printf ("Encrypted text: %sn", $encrypted);
You can get and set the encryption values also afterwards with the getEncryption() and setEncryption()
methods.
1 // Use the default AES encryption algorithm
2 $filter = new ZendFilterEncrypt(array(’adapter’ => ’BlockCipher’));
3 $filter->setKey(’encryption key’);
4 var_dump($filter->getEncryption());
5
6 // Will print:
7 //array(4) {
8 // ["key_iteration"]=>
9 // int(5000)
10 // ["algorithm"]=>
11 // string(3) "aes"
12 // ["hash"]=>
13 // string(6) "sha256"
14 // ["key"]=>
15 // string(14) "encryption key"
16 //}
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 $key = ’encryption key’;
2 $text = ’message to encrypt’;
3
4 // use the default adapter that is BlockCipher
5 $filter = new ZendFilterEncrypt();
6 $filter->setKey(’encryption key’);
7 for ($i=0; $i < 10; $i++) {
8 printf("%d) %sn", $i, $filter->filter($text));
9 }
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 // use the default adapter that is BlockCipher
2 $filter = new ZendFilterEncrypt();
3 $filter->setKey(’encryption key’);
4 $filter->setVector(’12345678901234567890’);
5 printf("%sn", $filter->filter(’message’));
6
7 // output:
8 // 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.
121.9. Encrypt and Decrypt 515
Zend Framework 2 Documentation, Release 2.3.1dev
121.9.4 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 $content = ’04636a6cb8276fad0787a2e187803b6557f77825d5ca6ed4392be702b9754bb3MTIzNDU2Nzg5MDEyMzQ1NgZ+z
2 // use the default adapter that is BlockCipher
3 $filter = new ZendFilterDecrypt();
4 $filter->setKey(’encryption key’);
5 printf("Decrypt: %sn", $filter->filter($content));
6
7 // output:
8 // 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().
121.9.5 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 // Use openssl and provide a private key
2 $filter = new ZendFilterEncrypt(array(
3 ’adapter’ => ’openssl’,
4 ’private’ => ’/path/to/mykey/private.pem’
5 ));
6
7 // of course you can also give the public keys at initiation
8 $filter->setPublicKey(array(
9 ’/public/key/path/first.pem’,
10 ’/public/key/path/second.pem’
11 ));
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 // Use openssl and provide a private key
2 $filter = new ZendFilterEncrypt(array(
3 ’adapter’ => ’openssl’,
4 ’private’ => ’/path/to/mykey/private.pem’
5 ));
6
7 // of course you can also give the public keys at initiation
8 $filter->setPublicKey(array(
516 Chapter 121. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.3.1dev
9 ’/public/key/path/first.pem’,
10 ’/public/key/path/second.pem’
11 ));
12 $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 // Use openssl and provide a private key
2 $filter = new ZendFilterEncrypt(array(
3 ’adapter’ => ’openssl’,
4 ’private’ => ’/path/to/mykey/private.pem’
5 ));
6
7 // of course you can also give the public keys at initiation
8 $filter->setPublicKey(array(
9 ’/public/key/path/first.pem’,
10 ’/public/key/path/second.pem’
11 ));
12 $filter->setPassphrase(’mypassphrase’);
13
14 $encrypted = $filter->filter(’text_to_be_encoded’);
15 $envelope = $filter->getEnvelopeKey();
16 print $encrypted;
17
18 // For decryption look at the Decrypt filter
121.9.6 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 // Use openssl and provide a private key
2 $filter = new ZendFilterEncrypt(array(
3 ’adapter’ => ’openssl’,
4 ’private’ => ’/path/to/mykey/private.pem’,
5 ’public’ => ’/public/key/path/public.pem’,
6 ’package’ => true
7 ));
8
9 $encrypted = $filter->filter(’text_to_be_encoded’);
10 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 com-
pression. But, and this is the negative aspect of this feature, the encrypted value can now only be decrypted by using
ZendFilterEncrypt.
121.9. Encrypt and Decrypt 517
Zend Framework 2 Documentation, Release 2.3.1dev
121.9.7 Compressing Content
Based on the original value, the encrypted value can be a very large string. To reduce the value
ZendFilterEncrypt allows the usage of compression.
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 // Use basic compression adapter
2 $filter1 = new ZendFilterEncrypt(array(
3 ’adapter’ => ’openssl’,
4 ’private’ => ’/path/to/mykey/private.pem’,
5 ’public’ => ’/public/key/path/public.pem’,
6 ’package’ => true,
7 ’compression’ => ’bz2’
8 ));
9
10 // Use basic compression adapter
11 $filter2 = new ZendFilterEncrypt(array(
12 ’adapter’ => ’openssl’,
13 ’private’ => ’/path/to/mykey/private.pem’,
14 ’public’ => ’/public/key/path/public.pem’,
15 ’package’ => true,
16 ’compression’ => array(’adapter’ => ’zip’, ’target’ => ’usrtmptmp.zip’)
17 ));
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.
121.9.8 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 // Use openssl and provide a private key
2 $filter = new ZendFilterDecrypt(array(
3 ’adapter’ => ’openssl’,
4 ’private’ => ’/path/to/mykey/private.pem’
5 ));
6
7 // of course you can also give the envelope keys at initiation
8 $filter->setEnvelopeKey(array(
9 ’/key/from/encoder/first.pem’,
10 ’/key/from/encoder/second.pem’
11 ));
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 // Use openssl and provide a private key
2 $filter = new ZendFilterDecrypt(array(
3 ’adapter’ => ’openssl’,
518 Chapter 121. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.3.1dev
4 ’private’ => ’/path/to/mykey/private.pem’
5 ));
6
7 // of course you can also give the envelope keys at initiation
8 $filter->setEnvelopeKey(array(
9 ’/key/from/encoder/first.pem’,
10 ’/key/from/encoder/second.pem’
11 ));
12 $filter->setPassphrase(’mypassphrase’);
At last, decode the content. Our complete example for decrypting the previously encrypted content looks like this.
1 // Use openssl and provide a private key
2 $filter = new ZendFilterDecrypt(array(
3 ’adapter’ => ’openssl’,
4 ’private’ => ’/path/to/mykey/private.pem’
5 ));
6
7 // of course you can also give the envelope keys at initiation
8 $filter->setEnvelopeKey(array(
9 ’/key/from/encoder/first.pem’,
10 ’/key/from/encoder/second.pem’
11 ));
12 $filter->setPassphrase(’mypassphrase’);
13
14 $decrypted = $filter->filter(’encoded_text_normally_unreadable’);
15 print $decrypted;
orphan
121.10 HtmlEntities
Returns the string $value, converting characters to their corresponding HTML entity equivalents where they exist.
121.10.1 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.
121.10. HtmlEntities 519
Zend Framework 2 Documentation, Release 2.3.1dev
121.10.2 Basic Usage
See the following example for the default behavior of this filter.
1 $filter = new ZendFilterHtmlEntities();
2
3 print $filter->filter(’<’);
121.10.3 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 $input = "A ’single’ and " . ’"double"’;
4 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 $input = "A ’single’ and " . ’"double"’;
4 print $filter->filter($input);
The above example returns A ’single’ and &quot;double&quot;. Notice that "double" quotes are fil-
tered while ’single’ quotes are not altered.
1 $filter = new ZendFilterHtmlEntities(array(’quotestyle’ => ENT_NOQUOTES));
2
3 $input = "A ’single’ and " . ’"double"’;
4 print $filter->filter($input);
The above example returns A ’single’ and "double". Notice that neither "double" or ’single’ quotes
are altered.
121.10.4 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 $filter->setQuoteStyle(ENT_QUOTES);
4 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 $filter->setQuoteStyle(ENT_QUOTES);
4 print $filter->getQuoteStyle(ENT_QUOTES);
520 Chapter 121. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.3.1dev
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 $filter->setQuoteStyle(ENT_QUOTES);
4 print $filter->getQuoteStyle(ENT_QUOTES);
orphan
121.11 Int
ZendFilterInt allows you to transform a scalar value which contains into an integer.
121.11.1 Supported Options
There are no additional options for ZendFilterInt.
121.11.2 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’.
orphan
121.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.
121.12.1 Supported Options
The following options are supported for ZendFilterNull:
• type: The variable type which should be supported.
121.12.2 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.
121.11. Int 521
Zend Framework 2 Documentation, Release 2.3.1dev
1 $filter = new ZendFilterNull();
2 $value = ’’;
3 $result = $filter->filter($value);
4 // returns null instead of the empty string
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.
121.12.3 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 // converts false to null
2 $filter = new ZendFilterNull(ZendFilterNull::BOOLEAN);
3
4 // converts false and 0 to null
5 $filter = new ZendFilterNull(
6 ZendFilterNull::BOOLEAN + ZendFilterNull::INTEGER
7 );
8
9 // converts false and 0 to null
10 $filter = new ZendFilterNull( array(
11 ZendFilterNull::BOOLEAN,
12 ZendFilterNull::INTEGER
13 ));
14
15 // converts false and 0 to null
16 $filter = new ZendFilterNull(array(
17 ’boolean’,
18 ’integer’,
19 ));
You can also give a Traversable or an array to set the wished types. To set types afterwards use setType().
orphan
522 Chapter 121. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.3.1dev
121.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 ex-
tension (Intl).
121.13.1 Supported Options
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. If unset, it will use
NumberFormatter::DEFAULT_STYLE as the default style.
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()
121.13.2 Basic Usage
1 $filter = new ZendI18nFilterNumberFormat("de_DE");
2 echo $filter->filter(1234567.8912346);
3 // Returns "1.234.567,891"
4
5 $filter = new ZendI18nFilterNumberFormat("en_US", NumberFormatter::PERCENT);
6 echo $filter->filter(0.80);
7 // Returns "80%"
8
9 $filter = new ZendI18nFilterNumberFormat("fr_FR", NumberFormatter::SCIENTIFIC);
10 echo $filter->filter(0.00123456789);
11 // Returns "1,23456789E-3"
orphan
121.14 PregReplace
ZendFilterPregReplace performs a search using regular expressions and replaces all found elements.
121.14.1 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.
121.13. NumberFormat 523
Zend Framework 2 Documentation, Release 2.3.1dev
121.14.2 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.
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 $filter = new ZendFilterPregReplace(array(
2 ’pattern’ => ’/bob/’,
3 ’replacement’ => ’john’,
4 ));
5 $input = ’Hi bob!’;
6
7 $filter->filter($input);
8 // 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 $filter = new ZendFilterPregReplace();
2 $filter->setMatchPattern(array(’bob’, ’Hi’))
3 ->setReplacement(array(’john’, ’Bye’));
4 $input = ’Hi bob!’;
5
6 $filter->filter($input);
7 // returns ’Bye john!’
For a more complex usage take a look into PHP‘s PCRE Pattern Chapter.
orphan
121.15 RealPath
This filter will resolve given links and pathnames and returns canonicalized absolute pathnames.
121.15.1 Supported Options
The following options are supported for ZendFilterRealPath:
• exists: This option defaults to TRUE which checks if the given path really exists.
121.15.2 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.
524 Chapter 121. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.3.1dev
1 $filter = new ZendFilterRealPath();
2 $path = ’/www/var/path/../../mypath’;
3 $filtered = $filter->filter($path);
4
5 // returns ’/www/mypath’
121.15.3 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.
1 $filter = new ZendFilterRealPath(false);
2 $path = ’/www/var/path/../../non/existing/path’;
3 $filtered = $filter->filter($path);
4
5 // returns ’/www/non/existing/path’
6 // even when file_exists or realpath would return false
orphan
121.16 StringToLower
This filter converts any input to be lowercased.
121.16.1 Supported Options
The following options are supported for ZendFilterStringToLower:
• encoding: This option can be used to set an encoding which has to be used.
121.16.2 Basic Usage
This is a basic example:
1 $filter = new ZendFilterStringToLower();
2
3 print $filter->filter(’SAMPLE’);
4 // returns "sample"
121.16.3 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 // using UTF-8
2 $filter = new ZendFilterStringToLower(’UTF-8’);
3
4 // or give an array which can be useful when using a configuration
5 $filter = new ZendFilterStringToLower(array(’encoding’ => ’UTF-8’));
121.16. StringToLower 525
Zend Framework 2 Documentation, Release 2.3.1dev
6
7 // or do this afterwards
8 $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.
orphan
121.17 StringToUpper
This filter converts any input to be uppercased.
121.17.1 Supported Options
The following options are supported for ZendFilterStringToUpper:
• encoding: This option can be used to set an encoding which has to be used.
121.17.2 Basic Usage
This is a basic example for using the StringToUpper filter:
1 $filter = new ZendFilterStringToUpper();
2
3 print $filter->filter(’Sample’);
4 // returns "SAMPLE"
121.17.3 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 // or do this afterwards
4 $filter->setEncoding(’ISO-8859-1’);
orphan
121.18 StringTrim
This filter modifies a given string such that certain characters are removed from the beginning and end.
526 Chapter 121. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.3.1dev
121.18.1 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.
121.18.2 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.
121.18.3 Default Behavior
1 $filter = new ZendFilterStringTrim(’:’);
2 // 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.
orphan
121.19 StripNewLines
This filter modifies a given string and removes all new line characters within that string.
121.19.1 Supported Options
There are no additional options for ZendFilterStripNewLines:
121.19.2 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.
orphan
121.19. StripNewLines 527
Zend Framework 2 Documentation, Release 2.3.1dev
121.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.
121.20.1 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 from the given content.
121.20.2 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.
121.20.3 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 $input = "A text with <br/> a <a href=’link.com’>link</a>";
4 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.
528 Chapter 121. Standard Filter Classes
Zend Framework 2 Documentation, Release 2.3.1dev
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.
121.20.4 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 $input = "A text with <br/> a <img src=’picture.com’ width=’100’>picture</img>";
4 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.
orphan
121.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.
121.21.1 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.
121.21.2 Basic Usage
See the following example for the default behaviour of this filter:
1 $filter = new ZendFilterUriNormalize(array(
2 ’enforcedScheme’ => ’https’
3 ));
4
5 echo $filter->filter(’www.example.com’);
As the result the string https://www.example.com will be output.
121.21. UriNormalize 529
Zend Framework 2 Documentation, Release 2.3.1dev
530 Chapter 121. Standard Filter Classes
CHAPTER 122
Word Filters
In addition to the standard set of filters, there are several classes specific to filtering word strings.
orphan
122.1 CamelCaseToDash
This filter modifies a given string such that ‘CamelCaseWords’ are converted to ‘Camel-Case-Words’.
122.1.1 Supported Options
There are no additional options for ZendFilterWordCamelCaseToDash:
122.1.2 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’.
orphan
122.2 CamelCaseToSeparator
This filter modifies a given string such that ‘CamelCaseWords’ are converted to ‘Camel Case Words’.
122.2.1 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.
531
Zend Framework 2 Documentation, Release 2.3.1dev
122.2.2 Basic Usage
A basic example of usage is below:
1 $filter = new ZendFilterWordCamelCaseToSeparator(’:’);
2 // or new ZendFilterWordCamelCaseToSeparator(array(’separator’ => ’:’));
3
4 print $filter->filter(’ThisIsMyContent’);
The above example returns ‘This:Is:My:Content’.
122.2.3 Default Behavior
1 $filter = new ZendFilterWordCamelCaseToSeparator();
2
3 print $filter->filter(’ThisIsMyContent’);
The above example returns ‘This Is My Content’.
orphan
122.3 CamelCaseToUnderscore
This filter modifies a given string such that ‘CamelCaseWords’ are converted to ‘Camel_Case_Words’.
122.3.1 Supported Options
There are no additional options for ZendFilterWordCamelCaseToUnderscore:
122.3.2 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’.
orphan
122.4 DashToCamelCase
This filter modifies a given string such that ‘words-with-dashes’ are converted to ‘WordsWithDashes’.
122.4.1 Supported Options
There are no additional options for ZendFilterWordDashToCamelCase:
532 Chapter 122. Word Filters
Zend Framework 2 Documentation, Release 2.3.1dev
122.4.2 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’.
orphan
122.5 DashToSeparator
This filter modifies a given string such that ‘words-with-dashes’ are converted to ‘words with dashes’.
122.5.1 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.
122.5.2 Basic Usage
A basic example of usage is below:
1 $filter = new ZendFilterWordDashToSeparator(’+’);
2 // or new ZendFilterWordCamelCaseToSeparator(array(’separator’ => ’+’));
3
4 print $filter->filter(’this-is-my-content’);
The above example returns ‘this+is+my+content’.
122.5.3 Default Behavior
1 $filter = new ZendFilterWordDashToSeparator();
2
3 print $filter->filter(’this-is-my-content’);
The above example returns ‘this is my content’.
orphan
122.6 DashToUnderscore
This filter modifies a given string such that ‘words-with-dashes’ are converted to ‘words_with_dashes’.
122.6.1 Supported Options
There are no additional options for ZendFilterWordDashToUnderscore:
122.5. DashToSeparator 533
Zend Framework 2 Documentation, Release 2.3.1dev
122.6.2 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’.
orphan
122.7 SeparatorToCamelCase
This filter modifies a given string such that ‘words with separators’ are converted to ‘WordsWithSeparators’.
122.7.1 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.
122.7.2 Basic Usage
A basic example of usage is below:
1 $filter = new ZendFilterWordSeparatorToCamelCase(’:’);
2 // or new ZendFilterWordSeparatorToCamelCase(array(’separator’ => ’:’));
3
4 print $filter->filter(’this:is:my:content’);
The above example returns ‘ThisIsMyContent’.
122.7.3 Default Behavior
1 $filter = new ZendFilterWordSeparatorToCamelCase();
2
3 print $filter->filter(’this is my content’);
The above example returns ‘ThisIsMyContent’.
orphan
122.8 SeparatorToDash
This filter modifies a given string such that ‘words with separators’ are converted to ‘words-with-separators’.
534 Chapter 122. Word Filters
Zend Framework 2 Documentation, Release 2.3.1dev
122.8.1 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.
122.8.2 Basic Usage
A basic example of usage is below:
1 $filter = new ZendFilterWordSeparatorToDash(’:’);
2 // or new ZendFilterWordSeparatorToDash(array(’separator’ => ’:’));
3
4 print $filter->filter(’this:is:my:content’);
The above example returns ‘this-is-my-content’.
122.8.3 Default Behavior
1 $filter = new ZendFilterWordSeparatorToDash();
2
3 print $filter->filter(’this is my content’);
The above example returns ‘this-is-my-content’.
orphan
122.9 SeparatorToSeparator
This filter modifies a given string such that ‘words with separators’ are converted to ‘words-with-separators’.
122.9.1 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.
122.9.2 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’.
122.9. SeparatorToSeparator 535
Zend Framework 2 Documentation, Release 2.3.1dev
122.9.3 Default Behaviour
1 $filter = new ZendFilterWordSeparatorToSeparator();
2
3 print $filter->filter(’this is my content’);
The above example returns ‘this-is-my-content’.
orphan
122.10 UnderscoreToCamelCase
This filter modifies a given string such that ‘words_with_underscores’ are converted to ‘WordsWithUnderscores’.
122.10.1 Supported Options
There are no additional options for ZendFilterWordUnderscoreToCamelCase:
122.10.2 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’.
orphan
122.11 UnderscoreToSeparator
This filter modifies a given string such that ‘words_with_underscores’ are converted to ‘words with underscores’.
122.11.1 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.
122.11.2 Basic usage
A basic example of usage is below:
1 $filter = new ZendFilterWordUnderscoreToSeparator(’+’);
2 // or new ZendFilterWordCamelCaseToSeparator(array(’separator’ => ’+’));
3
4 print $filter->filter(’this_is_my_content’);
The above example returns ‘this+is+my+content’.
536 Chapter 122. Word Filters
Zend Framework 2 Documentation, Release 2.3.1dev
122.11.3 Default Behavior
1 $filter = new ZendFilterWordUnderscoreToSeparator();
2
3 print $filter->filter(’this_is_my_content’);
The above example returns ‘this is my content’.
orphan
122.12 UnderscoreToDash
This filter modifies a given string such that ‘words_with_underscores’ are converted to ‘words-with-underscores’.
122.12.1 Supported Options
There are no additional options for ZendFilterWordUnderscoreToDash:
122.12.2 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’.
122.12. UnderscoreToDash 537
Zend Framework 2 Documentation, Release 2.3.1dev
538 Chapter 122. Word Filters
CHAPTER 123
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.
orphan
123.1 Decrypt
TODO
orphan
123.2 Encrypt
TODO
orphan
123.3 Lowercase
TODO
orphan
123.4 Rename
ZendFilterFileRename can be used to rename a file and/or move a file to a new path.
539
Zend Framework 2 Documentation, Release 2.3.1dev
123.4.1 Supported Options
The following set of options are supported:
• target (string) default: "*" Target filename or directory, the new name of the source file.
• 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 target path, a
ZendFilterExceptionRuntimeException will be thrown.
• randomize (boolean) default: false Shall target files have a random postfix attached? The ran-
dom 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.
123.4.2 Usage Examples
Move all filtered files to a different directory:
1 // ’target’ option is assumed if param is a string
2 $filter = new ZendFilterFileRename("/tmp/");
3 echo $filter->filter("./myfile.txt");
4 // File has been moved to "/tmp/myfile.txt"
Rename all filtered files to a new name:
1 $filter = new ZendFilterFileRename("/tmp/newfile.txt");
2 echo $filter->filter("./myfile.txt");
3 // File has been renamed to "/tmp/newfile.txt"
Move to a new path and randomize file names:
1 $filter = new ZendFilterFileRename(array(
2 "target" => "/tmp/newfile.txt",
3 "randomize" => true,
4 ));
5 echo $filter->filter("./myfile.txt");
6 // File has been renamed to "/tmp/newfile_4b3403665fea6.txt"
Configure different options for several possible source files:
1 $filter = new ZendFilterFileRename(array(
2 array(
3 "source" => "fileA.txt"
4 "target" => "/dest1/newfileA.txt",
5 "overwrite" => true,
6 ),
7 array(
8 "source" => "fileB.txt"
9 "target" => "/dest2/newfileB.txt",
10 "randomize" => true,
11 ),
12 ));
540 Chapter 123. File Filter Classes
Zend Framework 2 Documentation, Release 2.3.1dev
13 echo $filter->filter("fileA.txt");
14 // File has been renamed to "/dest1/newfileA.txt"
15 echo $filter->filter("fileB.txt");
16 // File has been renamed to "/dest2/newfileB_4b3403665fea6.txt"
123.4.3 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.
orphan
123.5 RenameUpload
ZendFilterFileRenameUpload can be used to rename or move an uploaded file to a new path.
123.5.1 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 target path, a
ZendFilterExceptionRuntimeException will be thrown.
• randomize (boolean) default: false Shall target files have a random postfix attached? The ran-
dom 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: false When true, the uploaded file will maintains its
original extension if not specified.
For example, if the uploaded file is "file.txt" and the target is something like "mynewfile", the
upload will be renamed to "mynewfile.txt".
123.5. RenameUpload 541
Zend Framework 2 Documentation, Release 2.3.1dev
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.
123.5.2 Usage Examples
Move all filtered files to a different directory:
1 use ZendHttpPhpEnvironmentRequest;
2
3 $request = new Request();
4 $files = $request->getFiles();
5 // i.e. $files[’my-upload’][’tmp_name’] === ’/tmp/php5Wx0aJ’
6 // i.e. $files[’my-upload’][’name’] === ’myfile.txt’
7
8 // ’target’ option is assumed if param is a string
9 $filter = new ZendFilterFileRenameUpload("./data/uploads/");
10 echo $filter->filter($files[’my-upload’]);
11 // File has been moved to "./data/uploads/php5Wx0aJ"
12
13 // ... or retain the uploaded file name
14 $filter->setUseUploadName(true);
15 echo $filter->filter($files[’my-upload’]);
16 // File has been moved to "./data/uploads/myfile.txt"
Rename all filtered files to a new name:
1 use ZendHttpPhpEnvironmentRequest;
2
3 $request = new Request();
4 $files = $request->getFiles();
5 // i.e. $files[’my-upload’][’tmp_name’] === ’/tmp/php5Wx0aJ’
6
7 $filter = new ZendFilterFileRename("./data/uploads/newfile.txt");
8 echo $filter->filter($files[’my-upload’]);
9 // File has been renamed to "./data/uploads/newfile.txt"
Move to a new path and randomize file names:
1 use ZendHttpPhpEnvironmentRequest;
2
3 $request = new Request();
4 $files = $request->getFiles();
5 // i.e. $files[’my-upload’][’tmp_name’] === ’/tmp/php5Wx0aJ’
6
7 $filter = new ZendFilterFileRename(array(
8 "target" => "./data/uploads/newfile.txt",
9 "randomize" => true,
10 ));
11 echo $filter->filter($files[’my-upload’]);
12 // File has been renamed to "./data/uploads/newfile_4b3403665fea6.txt"
orphan
542 Chapter 123. File Filter Classes
Zend Framework 2 Documentation, Release 2.3.1dev
123.6 Uppercase
TODO
123.6. Uppercase 543
Zend Framework 2 Documentation, Release 2.3.1dev
544 Chapter 123. File Filter Classes
CHAPTER 124
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 // Create a filter chain and add filters to the chain
2 $filterChain = new ZendFilterFilterChain();
3 $filterChain->attach(new ZendI18nFilterAlpha())
4 ->attach(new ZendFilterStringToLower());
5
6 // Filter the username
7 $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.
124.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 // Create a filter chain and add filters to the chain
2 $filterChain = new ZendFilterFilterChain();
3 $filterChain->attach(new ZendI18nFilterAlpha())
4 ->attach(new ZendFilterStringToLower(), 500);
124.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
filter within the FilterPluginManager. The second parameter takes any options for creating the filter instance.
The third parameter is the priority.
545
Zend Framework 2 Documentation, Release 2.3.1dev
1 // Create a filter chain and add filters to the chain
2 $filterChain = new ZendFilterFilterChain();
3 $filterChain->attachByName(’alpha’)
4 ->attachByName(’stringtolower’, array(’encoding’ => ’utf-8’), 500);
The following example shows how to add a custom filter to the FilterPluginManager and the FilterChain.
1 $filterChain = new ZendFilterFilterChain();
2 $filterChain->getPluginManager()->setInvokableClass(
3 ’myNewFilter’, ’MyCustomFilterMyNewFilter’
4 );
5 $filterChain->attachByName(’alpha’)
6 ->attachByName(’myNewFilter’);
You can also add your own FilterPluginManager implementation.
1 $filterChain = new ZendFilterFilterChain();
2 $filterChain->setPluginManager(new MyFilterPluginManager());
3 $filterChain->attach(new ZendI18nFilterAlpha())
4 ->attach(new MyCustomFilterMyNewFilter());
546 Chapter 124. Filter Chains
CHAPTER 125
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 call-
ing filter() on the object instance.
125.1 Transforming MixedCase and camelCaseText to another format
1 $inflector = new ZendFilterInflector(’pages/:page.:suffix’);
2 $inflector->setRules(array(
3 ’:page’ => array(’WordCamelCaseToDash’, ’StringToLower’),
4 ’suffix’ => ’html’,
5 ));
6
7 $string = ’camelCasedWords’;
8 $filtered = $inflector->filter(array(’page’ => $string));
9 // pages/camel-cased-words.html
10
11 $string = ’this_is_not_camel_cased’;
12 $filtered = $inflector->filter(array(’page’ => $string));
13 // pages/this_is_not_camel_cased.html
125.1.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.
As an example, you can use any ZendFilter concrete implementations; however, instead of referring to
them as ‘ZendI18nFilterAlpha‘ or ‘ZendFilterStringToLower‘, you’d specify only ‘Alpha‘
or ‘StringToLower‘.
547
Zend Framework 2 Documentation, Release 2.3.1dev
125.1.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 // use ZendFilterWordCamelCaseToDash as a rule
2 $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 $filters = $inflector->getPluginManager();
2 $filters->addInvokableClass(’mungify’, ’MyCustomFilterMungify’);
125.1.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 // Via constructor:
2 $inflector = new ZendFilterInflector(’#foo/#bar.#sfx’, null, ’#’);
3
4 // Via accessor:
5 $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 class Foo
2 {
3 /**
4 * @var string Inflector target
5 */
6 protected $_target = ’foo/:bar/:baz.:suffix’;
7
8 /**
9 * Constructor
10 * @return void
11 */
12 public function __construct()
13 {
14 $this->_inflector = new ZendFilterInflector();
15 $this->_inflector->setTargetReference($this->_target);
16 }
17
18 /**
548 Chapter 125. ZendFilterInflector
Zend Framework 2 Documentation, Release 2.3.1dev
19 * Set target; updates target in inflector
20 *
21 * @param string $target
22 * @return Foo
23 */
24 public function setTarget($target)
25 {
26 $this->_target = $target;
27 return $this;
28 }
29 }
125.1.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-by-
one, 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.
125.2 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 $inflector = new ZendFilterInflector(’:script.:suffix’);
2 $inflector->setStaticRule(’suffix’, ’phtml’);
3
4 // change it later:
5 $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 class Foo
2 {
3 /**
4 * @var string Suffix
5 */
6 protected $_suffix = ’phtml’;
7
8 /**
9 * Constructor
10 * @return void
11 */
12 public function __construct()
13 {
14 $this->_inflector = new ZendFilterInflector(’:script.:suffix’);
125.2. Static Rules 549
Zend Framework 2 Documentation, Release 2.3.1dev
15 $this->_inflector->setStaticRuleReference(’suffix’, $this->_suffix);
16 }
17
18 /**
19 * Set suffix; updates suffix static rule in inflector
20 *
21 * @param string $suffix
22 * @return Foo
23 */
24 public function setSuffix($suffix)
25 {
26 $this->_suffix = $suffix;
27 return $this;
28 }
29 }
125.3 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 // Set rule to use ZendFilterWordCamelCaseToDash filter
4 $inflector->setFilterRule(’script’, ’WordCamelCaseToDash’);
5
6 // Add rule to lowercase string
7 $inflector->addFilterRule(’script’, new ZendFilterStringToLower());
8
9 // Set rules en-masse
10 $inflector->setFilterRule(’script’, array(
11 ’WordCamelCaseToDash’,
12 new ZendFilterStringToLower()
13 ));
125.4 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
550 Chapter 125. ZendFilterInflector
Zend Framework 2 Documentation, Release 2.3.1dev
to the following notation:
• ‘:’ prefix: filter rules.
• No prefix: static rule.
Setting Multiple Rules at Once
1 // Could also use setRules() with this notation:
2 $inflector->addRules(array(
3 // filter rules:
4 ’:controller’ => array(’CamelCaseToUnderscore’,’StringToLower’),
5 ’:action’ => array(’CamelCaseToUnderscore’,’StringToLower’),
6
7 // Static rule:
8 ’suffix’ => ’phtml’
9 ));
125.4.1 Utility Methods
ZendFilterInflector has a number of utility methods for retrieving and setting the plugin loader, manipu-
lating 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 excep-
tion 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.
125.4.2 Using a Traversable or an array
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.
• 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().
125.4. Setting Many Rules At Once 551
Zend Framework 2 Documentation, Release 2.3.1dev
125.5 Example
1 // With the constructor:
2 $options; // implements Traversable
3 $inflector = new ZendFilterInflector($options);
4
5 // Or with setOptions():
6 $inflector = new ZendFilterInflector();
7 $inflector->setOptions($options);
552 Chapter 125. ZendFilterInflector
CHAPTER 126
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 echo StaticFilter::execute(’"’,
2 ’HtmlEntities’,
3 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 $pluginManager = StaticFilter::getPluginManager()->setInvokableClass(
2 ’myNewFilter’, ’MyCustomFilterMyNewFilter’
3 );
4
5 StaticFilter::setPluginManager(new MyFilterPluginManager());
This is useful when adding custom filters to be used by the StaticFilter.
553
Zend Framework 2 Documentation, Release 2.3.1dev
554 Chapter 126. Using the StaticFilter
CHAPTER 127
Writing Filters
ZendFilter supplies a set of commonly needed filters, but developers will often need to write cus-
tom 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.
127.1 Example
The following example demonstrates how to write a custom filter:
1 namespace ApplicationFilter;
2
3 use ZendFilterFilterInterface;
4
5 class MyFilter implements FilterInterface
6 {
7 public function filter($value)
8 {
9 // perform some transformation upon $value to arrive on $valueFiltered
10
11 return $valueFiltered;
12 }
13 }
To attach an instance of the filter defined above to a filter chain:
1 $filterChain = new ZendFilterFilterChain();
2 $filterChain->attach(new ApplicationFilterMyFilter());
555
Zend Framework 2 Documentation, Release 2.3.1dev
556 Chapter 127. Writing Filters
CHAPTER 128
Introduction
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 def-
inition, 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.
557
Zend Framework 2 Documentation, Release 2.3.1dev
558 Chapter 128. Introduction
CHAPTER 129
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.
129.1 Programmatic Form Creation
If nothing else, you can simply start creating elements, fieldsets, and forms and wiring them together.
1 use ZendCaptcha;
2 use ZendFormElement;
3 use ZendFormFieldset;
4 use ZendFormForm;
5 use ZendInputFilterInput;
6 use ZendInputFilterInputFilter;
7
8 $name = new Element(’name’);
9 $name->setLabel(’Your name’);
10 $name->setAttributes(array(
11 ’type’ => ’text’
12 ));
13
14 $email = new ElementEmail(’email’);
15 $email->setLabel(’Your email address’);
16
17 $subject = new Element(’subject’);
18 $subject->setLabel(’Subject’);
19 $subject->setAttributes(array(
20 ’type’ => ’text’
21 ));
22
23 $message = new ElementTextarea(’message’);
24 $message->setLabel(’Message’);
25
26 $captcha = new ElementCaptcha(’captcha’);
27 $captcha->setCaptcha(new CaptchaDumb());
28 $captcha->setLabel(’Please verify you are human’);
559
Zend Framework 2 Documentation, Release 2.3.1dev
29
30 $csrf = new ElementCsrf(’security’);
31
32 $send = new Element(’send’);
33 $send->setValue(’Submit’);
34 $send->setAttributes(array(
35 ’type’ => ’submit’
36 ));
37
38
39 $form = new Form(’contact’);
40 $form->add($name);
41 $form->add($email);
42 $form->add($subject);
43 $form->add($message);
44 $form->add($captcha);
45 $form->add($csrf);
46 $form->add($send);
47
48 $nameInput = new Input(’name’);
49 // configure input... and all others
50 $inputFilter = new InputFilter();
51 // 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 $sender = new Fieldset(’sender’);
2 $sender->add($name);
3 $sender->add($email);
4
5 $details = new Fieldset(’details’);
6 $details->add($subject);
7 $details->add($message);
8
9 $form = new Form(’contact’);
10 $form->add($sender);
11 $form->add($details);
12 $form->add($captcha);
13 $form->add($csrf);
14 $form->add($send);
Regardless of approach, as you can see, this can be tedious.
129.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.
1 use ZendFormFactory;
2
3 $factory = new Factory();
4 $form = $factory->createForm(array(
5 ’hydrator’ => ’ZendStdlibHydratorArraySerializable’,
6 ’elements’ => array(
560 Chapter 129. Quick Start
Zend Framework 2 Documentation, Release 2.3.1dev
7 array(
8 ’spec’ => array(
9 ’name’ => ’name’,
10 ’options’ => array(
11 ’label’ => ’Your name’,
12 ),
13 ’type’ => ’Text’,
14 )
15 ),
16 array(
17 ’spec’ => array(
18 ’type’ => ’ZendFormElementEmail’,
19 ’name’ => ’email’,
20 ’options’ => array(
21 ’label’ => ’Your email address’,
22 )
23 ),
24 ),
25 array(
26 ’spec’ => array(
27 ’name’ => ’subject’,
28 ’options’ => array(
29 ’label’ => ’Subject’,
30 ),
31 ’type’ => ’Text’,
32 ),
33 ),
34 array(
35 ’spec’ => array(
36 ’type’ => ’ZendFormElementTextarea’,
37 ’name’ => ’message’,
38 ’options’ => array(
39 ’label’ => ’Message’,
40 )
41 ),
42 ),
43 array(
44 ’spec’ => array(
45 ’type’ => ’ZendFormElementCaptcha’,
46 ’name’ => ’captcha’,
47 ’options’ => array(
48 ’label’ => ’Please verify you are human.’,
49 ’captcha’ => array(
50 ’class’ => ’Dumb’,
51 ),
52 ),
53 ),
54 ),
55 array(
56 ’spec’ => array(
57 ’type’ => ’ZendFormElementCsrf’,
58 ’name’ => ’security’,
59 ),
60 ),
61 array(
62 ’spec’ => array(
63 ’name’ => ’send’,
64 ’type’ => ’Submit’,
129.2. Creation via Factory 561
Zend Framework 2 Documentation, Release 2.3.1dev
65 ’attributes’ => array(
66 ’value’ => ’Submit’,
67 ),
68 ),
69 ),
70 ),
71 /* If we had fieldsets, they’d go here; fieldsets contain
72 * "elements" and "fieldsets" keys, and potentially a "type"
73 * key indicating the specific FieldsetInterface
74 * implementation to use.
75 ’fieldsets’ => array(
76 ),
77 */
78
79 // Configuration to pass on to
80 // ZendInputFilterFactory::createInputFilter()
81 ’input_filter’ => array(
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 $factory = new Factory();
4 $form = $factory->createForm(array(
5 ’hydrator’ => ’ZendStdlibHydratorArraySerializable’,
6 ’fieldsets’ => array(
7 array(
8 ’spec’ => array(
9 ’name’ => ’sender’,
10 ’elements’ => array(
11 array(
12 ’spec’ => array(
13 ’name’ => ’name’,
14 ’options’ => array(
15 ’label’ => ’Your name’,
16 ),
17 ’type’ => ’Text’
18 ),
19 ),
20 array(
21 ’spec’ => array(
22 ’type’ => ’ZendFormElementEmail’,
23 ’name’ => ’email’,
24 ’options’ => array(
25 ’label’ => ’Your email address’,
26 ),
27 ),
28 ),
29 ),
30 ),
31 ),
32 array(
33 ’spec’ => array(
34 ’name’ => ’details’,
35 ’elements’ => array(
36 array(
562 Chapter 129. Quick Start
Zend Framework 2 Documentation, Release 2.3.1dev
37 ’spec’ => array(
38 ’name’ => ’subject’,
39 ’options’ => array(
40 ’label’ => ’Subject’,
41 ),
42 ’type’ => ’Text’,
43 ),
44 ),
45 array(
46 ’spec’ => array(
47 ’name’ => ’message’,
48 ’type’ => ’ZendFormElementTextarea’,
49 ’options’ => array(
50 ’label’ => ’Message’,
51 ),
52 ),
53 ),
54 ),
55 ),
56 ),
57 ),
58 ’elements’ => array(
59 array(
60 ’spec’ => array(
61 ’type’ => ’ZendFormElementCaptcha’,
62 ’name’ => ’captcha’,
63 ’options’ => array(
64 ’label’ => ’Please verify you are human. ’,
65 ’captcha’ => array(
66 ’class’ => ’Dumb’,
67 ),
68 ),
69 ),
70 ),
71 array(
72 ’spec’ => array(
73 ’type’ => ’ZendFormElementCsrf’,
74 ’name’ => ’security’,
75 ),
76 ),
77 array(
78 ’spec’ => array(
79 ’name’ => ’send’,
80 ’type’ => ’Submit’,
81 ’attributes’ => array(
82 ’value’ => ’Submit’,
83 ),
84 ),
85 ),
86 ),
87 // Configuration to pass on to
88 // ZendInputFilterFactory::createInputFilter()
89 ’input_filter’ => array(
90 /* ... */
91 ),
92 ));
Note that the chief difference is nesting; otherwise, the information is basically the same.
129.2. Creation via Factory 563
Zend Framework 2 Documentation, Release 2.3.1dev
The chief benefits to using the Factory are allowing you to store definitions in configuration, and usage of significant
whitespace.
129.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 use ZendCaptchaAdapterInterface as CaptchaAdapter;
4 use ZendFormElement;
5 use ZendFormForm;
6
7 class ContactForm extends Form
8 {
9 protected $captcha;
10
11 public function __construct(CaptchaAdapter $captcha)
12 {
13
14 parent::__construct();
15
16 $this->captcha = $captcha;
17
18 // add() can take either an Element/Fieldset instance,
19 // or a specification, from which the appropriate object
20 // will be built.
21
22 $this->add(array(
23 ’name’ => ’name’,
24 ’options’ => array(
25 ’label’ => ’Your name’,
26 ),
27 ’type’ => ’Text’,
28 ));
29 $this->add(array(
30 ’type’ => ’ZendFormElementEmail’,
31 ’name’ => ’email’,
32 ’options’ => array(
33 ’label’ => ’Your email address’,
34 ),
35 ));
36 $this->add(array(
37 ’name’ => ’subject’,
38 ’options’ => array(
39 ’label’ => ’Subject’,
40 ),
41 ’type’ => ’Text’,
42 ));
43 $this->add(array(
44 ’type’ => ’ZendFormElementTextarea’,
45 ’name’ => ’message’,
46 ’options’ => array(
47 ’label’ => ’Message’,
564 Chapter 129. Quick Start
Zend Framework 2 Documentation, Release 2.3.1dev
48 ),
49 ));
50 $this->add(array(
51 ’type’ => ’ZendFormElementCaptcha’,
52 ’name’ => ’captcha’,
53 ’options’ => array(
54 ’label’ => ’Please verify you are human.’,
55 ’captcha’ => $this->captcha,
56 ),
57 ));
58 $this->add(new ElementCsrf(’security’));
59 $this->add(array(
60 ’name’ => ’send’,
61 ’type’ => ’Submit’,
62 ’attributes’ => array(
63 ’value’ => ’Submit’,
64 ),
65 ));
66
67 // We could also define the input filter here, or
68 // lazy-create it in the getInputFilter() method.
69 }
70 }
You’ll note that this example, the elements are added in the constructor. This is done to allow altering and/or config-
uring 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.
129.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 // If the form doesn’t define an input filter by default, inject one.
4 $form->setInputFilter(new ContactContactFilter());
5
6 // Get the data. In an MVC application, you might try:
7 $data = $request->getPost(); // for POST data
8 $data = $request->getQuery(); // for GET (or query string) data
9
10 $form->setData($data);
11
12 // Validate the form
13 if ($form->isValid()) {
14 $validatedData = $form->getData();
15 } else {
16 $messages = $form->getMessages();
17 }
You can get the raw data if you want, by accessing the composed input filter.
1 $filter = $form->getInputFilter();
2
129.4. Validating Forms 565
Zend Framework 2 Documentation, Release 2.3.1dev
3 $rawValues = $filter->getRawValues();
4 $nameRawValue = $filter->getRawValue(’name’);
129.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 use ZendFormElement;
4 use ZendInputFilterInputProviderInterface;
5 use ZendValidatorRegex as RegexValidator;
6 use ZendValidatorValidatorInterface;
7
8 class Color extends Element implements InputProviderInterface
9 {
10 /**
11 * Seed attributes
12 *
13 * @var array
14 */
15 protected $attributes = array(
16 ’type’ => ’color’,
17 );
18
19 /**
20 * @var ValidatorInterface
21 */
22 protected $validator;
23
24 /**
25 * Get validator
26 *
27 * @return ValidatorInterface
28 */
29 protected function getValidator()
30 {
31 if (null === $this->validator) {
32 $this->validator = new RegexValidator(’/^#[0-9a-fA-F]{6}$/’);
33 }
34 return $this->validator;
35 }
566 Chapter 129. Quick Start
Zend Framework 2 Documentation, Release 2.3.1dev
36
37 /**
38 * Provide default input rules for this element
39 *
40 * Attaches an email validator.
41 *
42 * @return array
43 */
44 public function getInputSpecification()
45 {
46 return array(
47 ’name’ => $this->getName(),
48 ’required’ => true,
49 ’filters’ => array(
50 array(’name’ => ’ZendFilterStringTrim’),
51 array(’name’ => ’ZendFilterStringToLower’),
52 ),
53 ’validators’ => array(
54 $this->getValidator(),
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 use ZendFormFieldset;
4 use ZendInputFilterInputFilterProviderInterface;
5 use ZendValidator;
6
7 class SenderFieldset extends Fieldset implements InputFilterProviderInterface
8 {
9 public function getInputFilterSpecification()
10 {
11 return array(
12 ’name’ => array(
13 ’required’ => true,
14 ’filters’ => array(
15 array(’name’ => ’ZendFilterStringTrim’),
16 ),
17 ),
18 ’email’ => array(
19 ’required’ => true,
20 ’filters’ => array(
21 array(’name’ => ’ZendFilterStringTrim’),
22 ),
23 ’validators’ => array(
24 new ValidatorEmailAddress(),
25 ),
26 ),
27 );
28 }
129.5. Hinting to the Input F
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2
zend framework 2

More Related Content

PDF
Zend framework tutorial
Olsi Zotaj
 
PDF
Db2 virtualization
bupbechanhgmail
 
PDF
The maxima book
ssuser0efdb21
 
PDF
Virtual box documentação tecnica
ALEXANDRE MEDINA
 
PDF
Redp4469
Ambresh Kothand
 
PDF
Oracle VM VirtualBox User Manual
MAIS Business Solutions
 
PDF
Os linux complete notes
Dreams Design
 
PDF
EMC NetWorker Module for Microsoft SQL Server Release 5.1 ...
webhostingguy
 
Zend framework tutorial
Olsi Zotaj
 
Db2 virtualization
bupbechanhgmail
 
The maxima book
ssuser0efdb21
 
Virtual box documentação tecnica
ALEXANDRE MEDINA
 
Redp4469
Ambresh Kothand
 
Oracle VM VirtualBox User Manual
MAIS Business Solutions
 
Os linux complete notes
Dreams Design
 
EMC NetWorker Module for Microsoft SQL Server Release 5.1 ...
webhostingguy
 

What's hot (20)

PDF
AIX 5L Differences Guide Version 5.3 Edition
IBM India Smarter Computing
 
PDF
Nvidia CUDA Programming Guide 1.0
Muhaza Liebenlito
 
PDF
Implementing IBM SmartCloud Entry on IBM PureFlex System
IBM India Smarter Computing
 
PDF
R installation and administration
Eric Espino
 
PDF
Administrator manual-e2
José Ignacio Salamanca
 
PDF
Cockpit esp
msabry7
 
PDF
Win plc engine-en
dreamtech2
 
PDF
Rapidminer 4.4-tutorial
Jeersson Maradiaga
 
PDF
An introduction to tivoli net view for os 390 v1r2 sg245224
Banking at Ho Chi Minh city
 
PDF
Sg248107 Implementing the IBM Storwize V3700
Wellington Torrejais da Silva
 
PDF
Implementing the ibm storwize v3700
Diego Alberto Tamayo
 
PDF
Migrating to netcool precision for ip networks --best practices for migrating...
Banking at Ho Chi Minh city
 
PDF
Akka java
dlvladescu
 
PDF
Deployment guide series maximo asset mng 7 1
Slađan Šehović
 
PDF
Automated provisioning using ibm tivoli intelligent orchestrator and enterpri...
Banking at Ho Chi Minh city
 
PDF
Ibm tivoli system automation for z os enterprise automation sg247308
Banking at Ho Chi Minh city
 
PDF
Faronics Deep Freeze Enterprise User Guide
Faronics
 
PDF
Automation using tivoli net view os 390 v1r3 and system automation os-390 v1r...
Banking at Ho Chi Minh city
 
PDF
Deep Freeze for Mac User Guide
Faronics
 
AIX 5L Differences Guide Version 5.3 Edition
IBM India Smarter Computing
 
Nvidia CUDA Programming Guide 1.0
Muhaza Liebenlito
 
Implementing IBM SmartCloud Entry on IBM PureFlex System
IBM India Smarter Computing
 
R installation and administration
Eric Espino
 
Administrator manual-e2
José Ignacio Salamanca
 
Cockpit esp
msabry7
 
Win plc engine-en
dreamtech2
 
Rapidminer 4.4-tutorial
Jeersson Maradiaga
 
An introduction to tivoli net view for os 390 v1r2 sg245224
Banking at Ho Chi Minh city
 
Sg248107 Implementing the IBM Storwize V3700
Wellington Torrejais da Silva
 
Implementing the ibm storwize v3700
Diego Alberto Tamayo
 
Migrating to netcool precision for ip networks --best practices for migrating...
Banking at Ho Chi Minh city
 
Akka java
dlvladescu
 
Deployment guide series maximo asset mng 7 1
Slađan Šehović
 
Automated provisioning using ibm tivoli intelligent orchestrator and enterpri...
Banking at Ho Chi Minh city
 
Ibm tivoli system automation for z os enterprise automation sg247308
Banking at Ho Chi Minh city
 
Faronics Deep Freeze Enterprise User Guide
Faronics
 
Automation using tivoli net view os 390 v1r3 and system automation os-390 v1r...
Banking at Ho Chi Minh city
 
Deep Freeze for Mac User Guide
Faronics
 
Ad

Similar to zend framework 2 (20)

PDF
Flask docs
Kunal Sangwan
 
PDF
Explorations in Parallel Distributed Processing: A Handbook of Models, Progra...
mustafa sarac
 
PDF
VBoxUserManual.pdf
vladvah77
 
PDF
Odoo development
Ramzi Hajjaji
 
PDF
An Introduction to MATLAB for Geoscientists.pdf
vishnuraj764102
 
PDF
Ug893 vivado-ide
Carmel Jackoby
 
PDF
Swi prolog-6.2.6
Omar Reyna Angeles
 
PDF
Manual
Elhabib Atiea
 
PDF
Manual de usuario de virtualbox
Ruben A Lozada S
 
PDF
Understanding Distributed Systems 2nd Edition 2nd Edition Roberto Vitillo
hunelibuzhan
 
PDF
An Introduction to Computer Science - python
LuisFernandoLozano5
 
PDF
Openobject developer
Ali Mashduqi
 
PDF
Debugger.pdf
BuTriLn
 
PDF
Openobject developer (2)
openerpwiki
 
PDF
Openobject developer1
openerpwiki
 
PDF
Openobject developer
openerpwiki
 
PDF
Open erp openobject-developer
openerpwiki
 
PDF
programación en prolog
Alex Pin
 
PDF
Embedded linux barco-20121001
Marc Leeman
 
Flask docs
Kunal Sangwan
 
Explorations in Parallel Distributed Processing: A Handbook of Models, Progra...
mustafa sarac
 
VBoxUserManual.pdf
vladvah77
 
Odoo development
Ramzi Hajjaji
 
An Introduction to MATLAB for Geoscientists.pdf
vishnuraj764102
 
Ug893 vivado-ide
Carmel Jackoby
 
Swi prolog-6.2.6
Omar Reyna Angeles
 
Manual de usuario de virtualbox
Ruben A Lozada S
 
Understanding Distributed Systems 2nd Edition 2nd Edition Roberto Vitillo
hunelibuzhan
 
An Introduction to Computer Science - python
LuisFernandoLozano5
 
Openobject developer
Ali Mashduqi
 
Debugger.pdf
BuTriLn
 
Openobject developer (2)
openerpwiki
 
Openobject developer1
openerpwiki
 
Openobject developer
openerpwiki
 
Open erp openobject-developer
openerpwiki
 
programación en prolog
Alex Pin
 
Embedded linux barco-20121001
Marc Leeman
 
Ad

Recently uploaded (20)

PPTX
Healing Routine Presentation.exercisepptx
eman youssif
 
PPTX
Understanding Value Education_Lect2.pptx
ssusera15ea5
 
PDF
Quarterly project_20250727_112257_0000.pdf
monteroemilia873
 
PPTX
The Journey of Self Refinement and self improvement
Muhammad Musawar Ali
 
PPTX
Combining Writing, Art, And Affirmations.pptx
eman youssif
 
PPTX
Holistic Development Role of Edu v5.pptx
ssusera15ea5
 
PPTX
Skincare: Know Your Skin, Build Your Routine
khushish167
 
PDF
Despre calibrare: O abordare structurată
Răzvan Deaconescu
 
PDF
Nep english aecc-2 about reading techniques
moharananilakantha87
 
PDF
The Human Edge: Why A.I. Can’t Steal Your Story!
vijitsrivastava083
 
PDF
Brown AesthetIc Minimalist Thesis Defense Presentation.pdf
EsharveerSingh
 
PPT
Life Skill_https://www.scribd.com/archive/plans?slideshare=true.ppt
machonvicoti
 
PPTX
Self Refinement According to Psychology
Muhammad Musawar Ali
 
PDF
Manual-of-Guerilla-Tactics To Protect You
bawga
 
PPTX
Your Personal Growth Journal journaling.pptx
eman youssif
 
PPTX
Escaping The Digital Noise And Finding Peace In Stillness.pptx
Peony Magazine
 
PPTX
Self-Care Toolbox.advices and developmentpptx
eman youssif
 
PPTX
Human_Self_Exploration1_Lecture-III.pptx
ssusera15ea5
 
PDF
Omica Pageant 2025- Premier beauty pageant platform
OmicaPageant
 
PPTX
Healing Portfolio Presentation.exercisepptx
eman youssif
 
Healing Routine Presentation.exercisepptx
eman youssif
 
Understanding Value Education_Lect2.pptx
ssusera15ea5
 
Quarterly project_20250727_112257_0000.pdf
monteroemilia873
 
The Journey of Self Refinement and self improvement
Muhammad Musawar Ali
 
Combining Writing, Art, And Affirmations.pptx
eman youssif
 
Holistic Development Role of Edu v5.pptx
ssusera15ea5
 
Skincare: Know Your Skin, Build Your Routine
khushish167
 
Despre calibrare: O abordare structurată
Răzvan Deaconescu
 
Nep english aecc-2 about reading techniques
moharananilakantha87
 
The Human Edge: Why A.I. Can’t Steal Your Story!
vijitsrivastava083
 
Brown AesthetIc Minimalist Thesis Defense Presentation.pdf
EsharveerSingh
 
Life Skill_https://www.scribd.com/archive/plans?slideshare=true.ppt
machonvicoti
 
Self Refinement According to Psychology
Muhammad Musawar Ali
 
Manual-of-Guerilla-Tactics To Protect You
bawga
 
Your Personal Growth Journal journaling.pptx
eman youssif
 
Escaping The Digital Noise And Finding Peace In Stillness.pptx
Peony Magazine
 
Self-Care Toolbox.advices and developmentpptx
eman youssif
 
Human_Self_Exploration1_Lecture-III.pptx
ssusera15ea5
 
Omica Pageant 2025- Premier beauty pageant platform
OmicaPageant
 
Healing Portfolio Presentation.exercisepptx
eman youssif
 

zend framework 2

  • 1. Zend Framework 2 Documentation Release 2.3.1dev Zend Technologies Ltd. March 22, 2014
  • 3. Contents 1 Overview 1 2 Installation 3 3 Getting Started with Zend Framework 2 5 3.1 Some assumptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 3.2 The tutorial application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 4 Getting started: A skeleton application 7 4.1 Using the Apache Web Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 4.2 Using the Built-in PHP CLI Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 4.3 Error reporting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 5 Routing and controllers 11 6 Create the controller 13 6.1 Initialise the view scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 7 Database and models 15 7.1 The database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 7.2 The model files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 7.3 Using ServiceManager to configure the table gateway and inject into the AlbumTable . . . . . . . . . 17 7.4 Back to the controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 7.5 Listing albums . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 8 Styling and Translations 21 9 Forms and actions 23 9.1 Adding new albums . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 9.2 Editing an album . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 9.3 Deleting an album . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 9.4 Ensuring that the home page displays the list of albums . . . . . . . . . . . . . . . . . . . . . . . . . 31 10 Conclusion 33 11 Getting Started with Zend Framework 2 35 11.1 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 11.2 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 i
  • 4. 12 A quick tour of the skeleton application 37 12.1 The dispatch cycle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 13 The MyTaskList application 39 13.1 The Checklist module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 13.2 The Module class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 14 The application’s pages 43 14.1 Routing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 14.2 The TaskController . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 14.3 The model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 14.4 Using Service Manager to configure the database credentials and inject into the controller . . . . . . 48 15 Listing tasks 51 15.1 Redirect the home page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 16 Styling 53 17 Adding new tasks 55 18 Editing a task 61 19 Deleting a task 65 20 Application Diagnostics 67 21 Step-by-step debugging 69 22 Conclusion 71 23 Zend Framework Tool (ZFTool) 73 23.1 Installation using Composer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 23.2 Manual installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 23.3 Without installation, using the PHAR file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 23.4 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 24 Learning Dependency Injection 77 24.1 Very brief introduction to Di. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 24.2 Simplest usage case (2 classes, one consumes the other) . . . . . . . . . . . . . . . . . . . . . . . . 77 24.3 Simplest Usage Case Without Type-hints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 24.4 Simplest usage case with Compiled Definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 24.5 Creating a precompiled definition for others to use . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 24.6 Using Multiple Definitions From Multiple Sources . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 24.7 Generating Service Locators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 25 Unit Testing a Zend Framework 2 application 87 25.1 Setting up the tests directory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 25.2 Bootstrapping your tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 25.3 Your first controller test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 25.4 A failing test case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 25.5 Configuring the service manager for the tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 25.6 Testing actions with POST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 25.7 Testing model entities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 25.8 Testing model tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 25.9 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 ii
  • 5. 26 Using the EventManager 101 26.1 Terminology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 26.2 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 26.3 Shared managers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 27 Wildcards 105 28 Listener aggregates 107 28.1 Introspecting results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 28.2 Short-circuiting listener execution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 28.3 Keeping it in order . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 28.4 Custom event objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 28.5 Putting it together: Implementing a simple caching system . . . . . . . . . . . . . . . . . . . . . . . 111 28.6 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 29 Advanced Configuration Tricks 115 29.1 System configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 29.2 Module Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 29.3 Configuration mapping table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120 29.4 Configuration Priority . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120 29.5 Manipulating merged configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120 29.6 Configuration merging workflow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 30 Using ZendNavigation in your Album Module 123 30.1 Preparation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 30.2 Setting Up ZendNavigation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 30.3 Configuring our Site Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124 30.4 Adding the Menu View Helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 30.5 Adding Breadcrumbs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 31 Using ZendPaginator in your Album Module 127 31.1 Preparation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 31.2 Modifying the AlbumTable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 31.3 Modifying the AlbumController . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 31.4 Updating the View Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 31.5 Creating the Pagination Control Partial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 32 Using the PaginationControl View Helper 135 33 Setting up a database adapter 137 33.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 33.2 Basic setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 33.3 Setting a static adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 34 Migration from Zend Framework 1 139 35 Namespacing Old Classes 141 35.1 Namespacing a ZF1 Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 35.2 HOWTO Namespace Your Models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 36 Running Zend Framework 2 and Zend Framework 1 in parallel 145 36.1 Use ZF2 in a ZF1 project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 36.2 Use ZF1 in a ZF2 project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 36.3 Run ZF1 and ZF2 together . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146 iii
  • 6. 37 Introduction to ZendAuthentication 147 37.1 Adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 37.2 Results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148 37.3 Identity Persistence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149 37.4 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152 38 Database Table Authentication 155 38.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 38.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 38.3 Advanced Usage: Persisting a DbTable Result Object . . . . . . . . . . . . . . . . . . . . . . . . . . 157 39 Digest Authentication 161 39.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 39.2 Specifics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 39.3 Identity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 40 HTTP Authentication Adapter 163 40.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 40.2 Design Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 40.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 40.4 Resolvers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164 40.5 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165 41 LDAP Authentication 167 41.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167 41.2 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167 41.3 The API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 41.4 Server Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170 41.5 Collecting Debugging Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 41.6 Common Options for Specific Servers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 42 Authentication Validator 175 42.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175 42.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175 43 Introduction to ZendBarcode 177 44 Barcode creation using ZendBarcodeBarcode class 179 44.1 Using ZendBarcodeBarcode::factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179 44.2 Drawing a barcode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180 44.3 Rendering a barcode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180 45 ZendBarcodeBarcode Objects 183 45.1 Common Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183 45.2 Common Additional Getters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185 45.3 Description of shipped barcodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185 46 ZendBarcode Renderers 193 46.1 Common Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 46.2 ZendBarcodeRendererImage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194 46.3 ZendBarcodeRendererPdf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194 47 ZendCacheStorageAdapter 195 47.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195 47.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195 47.3 Basic Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 iv
  • 7. 47.4 The StorageInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 47.5 The AvailableSpaceCapableInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198 47.6 The TotalSpaceCapableInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198 47.7 The ClearByNamespaceInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199 47.8 The ClearByPrefixInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199 47.9 The ClearExpiredInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199 47.10 The FlushableInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199 47.11 The IterableInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199 47.12 The OptimizableInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200 47.13 The TaggableInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200 47.14 The Apc Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200 47.15 The Dba Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201 47.16 The Filesystem Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202 47.17 The Memcached Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203 47.18 The Memory Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204 47.19 The WinCache Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 47.20 The XCache Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206 47.21 The ZendServerDisk Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207 47.22 The ZendServerShm Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207 47.23 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208 48 ZendCacheStorageCapabilities 211 48.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211 48.2 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211 48.3 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213 49 ZendCacheStoragePlugin 215 49.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 49.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 49.3 The ClearExpiredByFactor Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216 49.4 The ExceptionHandler Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216 49.5 The IgnoreUserAbort Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216 49.6 The OptimizeByFactor Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216 49.7 The Serializer Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217 49.8 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217 49.9 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217 50 ZendCachePattern 219 50.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219 50.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219 50.3 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219 51 ZendCachePatternCallbackCache 221 51.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 51.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 51.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 51.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222 51.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222 52 ZendCachePatternClassCache 223 52.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223 52.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223 52.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223 52.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223 52.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224 v
  • 8. 53 ZendCachePatternObjectCache 227 53.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227 53.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227 53.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227 53.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228 53.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228 54 ZendCachePatternOutputCache 231 54.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231 54.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231 54.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231 54.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231 54.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232 55 ZendCachePatternCaptureCache 233 55.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233 55.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233 55.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234 55.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234 55.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235 56 Introduction to ZendCaptcha 237 57 Captcha Operation 239 58 CAPTCHA Adapters 241 58.1 ZendCaptchaAbstractWord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241 58.2 ZendCaptchaDumb . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242 58.3 ZendCaptchaFiglet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242 58.4 ZendCaptchaImage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242 58.5 ZendCaptchaReCaptcha . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243 59 Introduction 245 59.1 Theory of Operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245 60 ZendCodeGenerator Reference 249 60.1 Abstract Classes and Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249 60.2 Concrete CodeGenerator Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250 61 ZendCodeGenerator Examples 257 62 Introduction to ZendConfig 265 62.1 Using ZendConfigConfig with a Reader Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265 62.2 Using ZendConfigConfig with a PHP Configuration File . . . . . . . . . . . . . . . . . . . . . . . 266 63 Theory of Operation 267 64 ZendConfigReader 269 64.1 ZendConfigReaderIni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269 64.2 ZendConfigReaderXml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270 64.3 ZendConfigReaderJson . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271 64.4 ZendConfigReaderYaml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272 65 ZendConfigWriter 275 65.1 ZendConfigWriterIni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275 65.2 ZendConfigWriterXml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276 vi
  • 9. 65.3 ZendConfigWriterPhpArray . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277 65.4 ZendConfigWriterJson . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278 65.5 ZendConfigWriterYaml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278 66 ZendConfigProcessor 281 66.1 ZendConfigProcessorConstant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281 66.2 ZendConfigProcessorFilter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281 66.3 ZendConfigProcessorQueue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282 66.4 ZendConfigProcessorToken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282 66.5 ZendConfigProcessorTranslator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283 67 The Factory 285 67.1 Loading configuration file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285 67.2 Storing configuration file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285 68 Introduction to ZendConsole 287 68.1 Writing console routes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287 68.2 Handling console requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289 68.3 Adding console usage info . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290 69 Console routes and routing 293 69.1 Router configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293 69.2 Basic route . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294 69.3 Catchall route . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298 69.4 Console routes cheat-sheet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299 70 Console-aware modules 301 70.1 Application banner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301 70.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302 70.3 Best practices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305 71 Console-aware action controllers 307 71.1 Handling console requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307 71.2 Sending output to console . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309 71.3 Are we in a console? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309 71.4 Reading values from console parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311 72 Console adapters 315 72.1 Retrieving console adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315 72.2 Using console adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316 73 Console prompts 319 73.1 Confirm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319 73.2 Line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320 73.3 Char . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320 73.4 Select . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321 74 Introduction 323 75 Declaring Getopt Rules 325 75.1 Declaring Options with the Short Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325 75.2 Declaring Options with the Long Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325 76 Fetching Options and Arguments 327 76.1 Handling Getopt Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327 76.2 Fetching Options by Name . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328 vii
  • 10. 76.3 Reporting Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328 76.4 Fetching Non-option Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328 77 Configuring ZendConsoleGetopt 331 77.1 Adding Option Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331 77.2 Adding Help Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331 77.3 Adding Option Aliases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332 77.4 Adding Argument Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332 77.5 Adding Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333 78 Introduction to ZendCrypt 335 79 Encrypt/decrypt using block ciphers 337 80 Key derivation function 339 80.1 Pbkdf2 adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339 80.2 SaltedS2k adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340 80.3 Scrypt adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340 81 Password 343 81.1 Bcrypt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343 81.2 Apache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344 82 Public key cryptography 347 82.1 Diffie-Hellman . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347 82.2 RSA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348 83 ZendDbAdapter 351 83.1 Creating an Adapter - Quickstart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351 83.2 Creating an Adapter Using Dependency Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352 83.3 Query Preparation Through ZendDbAdapterAdapter::query() . . . . . . . . . . . . . . . . . . . . 352 83.4 Query Execution Through ZendDbAdapterAdapter::query() . . . . . . . . . . . . . . . . . . . . . 353 83.5 Creating Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353 83.6 Using the Driver Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353 83.7 Using The Platform Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355 83.8 Using The Parameter Container . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356 83.9 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357 84 ZendDbResultSet 359 84.1 Quickstart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359 84.2 ZendDbResultSetResultSet and ZendDbResultSetAbstractResultSet . . . . . . . . . . . . . . . . 360 84.3 ZendDbResultSetHydratingResultSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360 85 ZendDbSql 363 85.1 ZendDbSqlSql (Quickstart) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363 85.2 ZendDbSql’s Select, Insert, Update and Delete . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364 85.3 ZendDbSqlSelect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364 85.4 ZendDbSqlInsert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367 85.5 ZendDbSqlUpdate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368 85.6 ZendDbSqlDelete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368 85.7 ZendDbSqlWhere & ZendDbSqlHaving . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368 86 ZendDbSqlDdl 375 86.1 Creating Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375 86.2 Altering Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376 86.3 Dropping Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376 viii
  • 11. 86.4 Executing DDL Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376 86.5 Currently Supported Data Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377 86.6 Currently Supported Constraint Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377 87 ZendDbTableGateway 379 87.1 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379 87.2 TableGateway Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381 88 ZendDbRowGateway 383 88.1 Quickstart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383 88.2 ActiveRecord Style Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384 89 ZendDbMetadata 385 89.1 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385 90 Dumping Variables 389 90.1 Example of dump() method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389 91 Introduction to ZendDi 391 91.1 Dependency Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391 91.2 Dependency Injection Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391 92 ZendDi Quickstart 393 93 ZendDi Definition 397 93.1 DefinitionList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397 93.2 RuntimeDefinition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397 93.3 CompilerDefinition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398 93.4 ClassDefinition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399 94 ZendDi InstanceManager 401 94.1 Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401 94.2 Preferences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 402 94.3 Aliases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403 95 ZendDi Configuration 405 96 ZendDi Debugging & Complex Use Cases 407 96.1 Debugging a DiC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407 96.2 Complex Use Cases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407 97 Introduction to ZendDom 411 98 ZendDomQuery 413 98.1 Theory of Operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413 98.2 Methods Available . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414 99 Introduction to ZendEscaper 417 99.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417 99.2 What ZendEscaper is not . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417 100Theory of Operation 419 100.1 The Problem with Inconsistent Functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419 100.2 Why Contextual Escaping? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 420 101Configuring ZendEscaper 423 ix
  • 12. 102Escaping HTML 425 102.1 Examples of Bad HTML Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425 102.2 Examples of Good HTML Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426 103Escaping HTML Attributes 427 103.1 Examples of Bad HTML Attribute Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427 103.2 Examples of Good HTML Attribute Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 428 104Escaping Javascript 431 104.1 Examples of Bad Javascript Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431 104.2 Examples of Good Javascript Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 432 105Escaping Cascading Style Sheets 433 105.1 Examples of Bad CSS Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433 105.2 Examples of Good CSS Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433 106Escaping URLs 435 106.1 Examples of Bad URL Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435 106.2 Examples of Good URL Escaping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435 107The EventManager 437 107.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 437 107.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 437 107.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440 107.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440 107.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 442 108Using Exceptions 447 108.1 Catching an Exception . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447 109Introduction to ZendFeed 449 109.1 Reading RSS Feed Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449 110Importing Feeds 451 110.1 Dumping the contents of a feed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451 111Retrieving Feeds from Web Pages 453 111.1 Find Feed Links . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 453 112Consuming an RSS Feed 455 112.1 Reading a feed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455 112.2 Get properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455 113Consuming an Atom Feed 457 113.1 Basic Use of an Atom Feed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457 114Consuming a Single Atom Entry 459 114.1 Reading a Single-Entry Atom Feed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459 115ZendFeed and Security 461 115.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461 115.2 Filtering data using HTMLPurifier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461 115.3 Escaping data using ZendEscaper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463 116ZendFeedReaderReader 465 116.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465 x
  • 13. 116.2 Importing Feeds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465 116.3 Retrieving Underlying Feed and Entry Sources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 466 116.4 Cache Support and Intelligent Requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467 116.5 Locating Feed URIs from Websites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 468 116.6 Attribute Collections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469 116.7 Retrieving Feed Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469 116.8 Retrieving Entry/Item Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 472 116.9 Extending Feed and Entry APIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 474 117ZendFeedWriterWriter 479 117.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479 117.2 Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479 117.3 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 480 117.4 Setting Feed Data Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 482 117.5 Setting Entry Data Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 484 118ZendFeedPubSubHubbub 487 118.1 What is PubSubHubbub? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 487 118.2 Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 487 118.3 ZendFeedPubSubHubbubPublisher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 488 118.4 ZendFeedPubSubHubbubSubscriber . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489 119ZendFileClassFileLocator 495 119.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495 119.2 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495 119.3 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495 120Introduction to ZendFilter 497 120.1 What is a filter? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497 120.2 Basic usage of filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497 120.3 Using the StaticFilter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498 120.4 Double filtering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498 121Standard Filter Classes 501 121.1 Alnum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501 121.2 Alpha . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502 121.3 BaseName . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502 121.4 Boolean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503 121.5 Callback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506 121.6 Compress and Decompress . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507 121.7 Digits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 512 121.8 Dir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 513 121.9 Encrypt and Decrypt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 513 121.10HtmlEntities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 519 121.11Int . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 521 121.12Null . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 521 121.13NumberFormat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 523 121.14PregReplace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 523 121.15RealPath . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 524 121.16StringToLower . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 525 121.17StringToUpper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 526 121.18StringTrim . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 526 121.19StripNewLines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 527 121.20StripTags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 528 121.21UriNormalize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 529 xi
  • 14. 122Word Filters 531 122.1 CamelCaseToDash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 531 122.2 CamelCaseToSeparator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 531 122.3 CamelCaseToUnderscore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 532 122.4 DashToCamelCase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 532 122.5 DashToSeparator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 533 122.6 DashToUnderscore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 533 122.7 SeparatorToCamelCase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 534 122.8 SeparatorToDash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 534 122.9 SeparatorToSeparator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 535 122.10UnderscoreToCamelCase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 536 122.11UnderscoreToSeparator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 536 122.12UnderscoreToDash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 537 123File Filter Classes 539 123.1 Decrypt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539 123.2 Encrypt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539 123.3 Lowercase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539 123.4 Rename . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539 123.5 RenameUpload . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 541 123.6 Uppercase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 543 124Filter Chains 545 124.1 Setting Filter Chain Order . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545 124.2 Using the Plugin Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545 125ZendFilterInflector 547 125.1 Transforming MixedCase and camelCaseText to another format . . . . . . . . . . . . . . . . . . . . 547 125.2 Static Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 549 125.3 Filter Inflector Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 550 125.4 Setting Many Rules At Once . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 550 125.5 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 552 126Using the StaticFilter 553 127Writing Filters 555 127.1 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 555 128Introduction 557 129Quick Start 559 129.1 Programmatic Form Creation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 559 129.2 Creation via Factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560 129.3 Factory-backed Form Extension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 564 129.4 Validating Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 565 129.5 Hinting to the Input Filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 566 129.6 Binding an object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 568 129.7 Rendering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 569 129.8 Validation Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 572 129.9 Using Annotations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 573 130Form Collections 577 130.1 Creating Fieldsets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 580 130.2 The Form Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 584 130.3 The Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 585 xii
  • 15. 130.4 The View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 585 130.5 Adding New Elements Dynamically . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 586 130.6 Validation groups for fieldsets and collection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 588 131File Uploading 591 131.1 Standard Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 591 131.2 File Post-Redirect-Get Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 594 131.3 HTML5 Multi-File Uploads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596 131.4 Upload Progress . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 597 131.5 Additional Info . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 601 132Advanced use of forms 603 132.1 Short names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 603 132.2 Creating custom elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 603 132.3 Handling dependencies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 607 132.4 The specific case of initializers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 609 133Form Elements 611 133.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 611 133.2 Element Base Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 611 133.3 Standard Elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 613 133.4 HTML5 Elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 629 134Form View Helpers 643 134.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 643 134.2 Standard Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 643 134.3 HTML5 Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 657 134.4 File Upload Progress Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661 135ZendHttp 663 135.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663 135.2 ZendHttp Request, Response and Headers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663 136The Request Class 665 136.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 665 136.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 665 136.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 666 136.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 666 136.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669 137The Response Class 671 137.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 671 137.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 671 137.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672 137.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672 137.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 674 138The Headers Class 677 138.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 677 138.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 677 138.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 678 138.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 678 138.5 ZendHttpHeaderHeaderInterface Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 680 138.6 ZendHttpHeaderAbstractAccept Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 680 138.7 ZendHttpHeaderAbstractDate Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 681 xiii
  • 16. 138.8 ZendHttpHeaderAbstractLocation Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 681 138.9 List of HTTP Header Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 682 138.10Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 687 139HTTP Client 689 139.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 689 139.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 689 139.3 Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 690 139.4 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 691 140HTTP Client - Connection Adapters 695 140.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 695 140.2 The Socket Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 695 140.3 The Proxy Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 698 140.4 The cURL Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 699 140.5 The Test Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 700 140.6 Creating your own connection adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 702 141HTTP Client - Advanced Usage 705 141.1 HTTP Redirections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705 141.2 Adding Cookies and Using Cookie Persistence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705 141.3 Setting Custom Request Headers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 707 141.4 File Uploads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 707 141.5 Sending Raw POST Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 708 141.6 HTTP Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 709 141.7 Sending Multiple Requests With the Same Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . 709 141.8 Data Streaming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 710 142HTTP Client - Static Usage 713 142.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 713 142.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 713 142.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 713 142.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 714 143Translating 715 143.1 Adding translations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 715 143.2 Supported formats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716 143.3 Setting a locale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716 143.4 Translating messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716 143.5 Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716 144I18n View Helpers 717 144.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 717 144.2 CurrencyFormat Helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 717 144.3 DateFormat Helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 719 144.4 NumberFormat Helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 720 144.5 Plural Helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 722 144.6 Translate Helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 723 144.7 TranslatePlural Helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 724 144.8 Abstract Translator Helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 725 145I18n Filters 727 145.1 Alnum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 727 145.2 Alpha . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 728 145.3 NumberFormat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 728 xiv
  • 17. 145.4 NumberParse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 729 146I18n Validators 731 147Float 733 147.1 Supported options for ZendI18nValidatorFloat . . . . . . . . . . . . . . . . . . . . . . . . . . . . 733 147.2 Simple float validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 733 147.3 Localized float validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 733 147.4 Int . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 734 148Introduction 735 149File Upload Input 739 149.1 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 739 150Introduction 741 151Basic Usage 743 151.1 Pretty-printing JSON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 743 152Advanced Usage 745 152.1 JSON Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 745 152.2 Encoding PHP objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 745 152.3 Internal Encoder/Decoder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 746 152.4 JSON Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 746 153XML to JSON conversion 747 153.1 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 747 154ZendJsonServer - JSON-RPC server 749 154.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 749 154.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 749 154.3 Advanced Details . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 751 155Introduction to ZendLdap 757 155.1 Theory of operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 757 156API overview 761 156.1 Configuration / options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 761 156.2 API Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 762 157ZendLdapLdap 763 157.1 ZendLdapCollection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 764 158ZendLdapAttribute 765 159ZendLdapConverterConverter 767 160ZendLdapDn 769 161ZendLdapFilter 773 162ZendLdapNode 777 163ZendLdapNodeRootDse 779 163.1 OpenLDAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 781 163.2 ActiveDirectory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 781 xv
  • 18. 163.3 eDirectory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 782 164ZendLdapNodeSchema 785 164.1 OpenLDAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 787 164.2 ActiveDirectory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 788 165ZendLdapLdifEncoder 789 166Usage Scenarios 791 166.1 Authentication scenarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791 166.2 Basic CRUD operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791 166.3 Extended operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 793 167Tools 795 167.1 Creation and modification of DN strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 795 167.2 Using the filter API to create search filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 795 167.3 Modify LDAP entries using the Attribute API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 795 168Object-oriented access to the LDAP tree using ZendLdapNode 797 168.1 Basic CRUD operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 797 168.2 Extended operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 797 168.3 Tree traversal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 797 169Getting information from the LDAP server 799 169.1 RootDSE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 799 169.2 Schema Browsing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 799 170Serializing LDAP data to and from LDIF 801 170.1 Serialize a LDAP entry to LDIF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 801 170.2 Deserialize a LDIF string into a LDAP entry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 802 171The AutoloaderFactory 805 171.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 805 171.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 805 171.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 806 171.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 806 171.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 806 172The StandardAutoloader 807 172.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 807 172.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 808 172.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 809 172.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 809 172.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 810 173The ClassMapAutoloader 811 173.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 811 173.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 811 173.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 812 173.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 812 173.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 813 174The ModuleAutoloader 815 174.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 815 174.2 Quickstart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 815 174.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 815 xvi
  • 19. 174.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 815 174.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 816 175The SplAutoloader Interface 817 175.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 817 175.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 817 175.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 818 175.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 818 175.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 819 176The PluginClassLoader 821 176.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821 176.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821 176.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 822 176.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 822 176.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 823 177The ShortNameLocator Interface 827 177.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 827 177.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 827 177.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 827 177.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 828 177.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 828 178The PluginClassLocator interface 829 178.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 829 178.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 829 178.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 829 178.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 829 178.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 830 179The Class Map Generator utility: bin/classmap_generator.php 831 179.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831 179.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831 179.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831 180ZendLog 833 180.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 833 180.2 Creating a Log . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 833 180.3 Logging Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 834 180.4 Destroying a Log . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 834 180.5 Using Built-in Priorities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 834 180.6 Understanding Log Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 835 180.7 Log PHP Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 835 181Writers 837 181.1 Writing to Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 837 181.2 Writing to Databases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 838 181.3 Writing to FirePHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 838 181.4 Stubbing Out the Writer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 839 181.5 Testing with the Mock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 839 181.6 Compositing Writers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 840 182Filters 841 182.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 841 xvii
  • 20. 182.2 Available filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 841 183Formatters 843 183.1 Simple Formatting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 843 183.2 Formatting to XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 843 183.3 Formatting to FirePhp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 844 184Introduction to ZendMail 845 184.1 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 845 184.2 Configuring the default sendmail transport . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 846 185ZendMailMessage 847 185.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 847 185.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 847 185.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 849 185.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 849 185.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 852 186ZendMailTransport 853 186.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 853 186.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 853 186.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 854 186.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 855 186.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 855 187ZendMailTransportSmtpOptions 857 187.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 857 187.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 857 187.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 859 187.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 859 187.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 860 188ZendMailTransportFileOptions 861 188.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 861 188.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 861 188.3 Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 861 188.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 861 188.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 862 189Introduction to ZendMath 863 189.1 Random number generator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 863 189.2 Big integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 864 190Overview 867 190.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 867 190.2 Theory of Operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 868 191Memory Manager 869 191.1 Creating a Memory Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 869 191.2 Managing Memory Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 869 191.3 Memory Manager Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 870 192Memory Objects 873 192.1 Movable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 873 192.2 Locked . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 873 192.3 Memory container ‘value’ property . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 873 xviii
  • 21. 192.4 Memory container interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 874 193ZendMime 877 193.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 877 193.2 Static Methods and Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 877 193.3 Instantiating ZendMime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 878 194ZendMimeMessage 879 194.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 879 194.2 Instantiation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 879 194.3 Adding MIME Parts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 879 194.4 Boundary handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 879 194.5 Parsing a string to create a ZendMimeMessage object . . . . . . . . . . . . . . . . . . . . . . . . . 880 194.6 Available methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 880 195ZendMimePart 881 195.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 881 195.2 Instantiation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 881 195.3 Methods for rendering the message part to a string . . . . . . . . . . . . . . . . . . . . . . . . . . . 881 195.4 Available methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 882 196Introduction to the Module System 883 196.1 The autoload_*.php Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 884 197The Module Manager 885 197.1 Module Manager Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 885 197.2 Module Manager Listeners . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 886 198The Module Class 889 198.1 A Minimal Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 889 198.2 A Typical Module Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 889 198.3 The “loadModules.post” Event . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 890 198.4 The MVC “bootstrap” Event . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 891 199The Module Autoloader 893 199.1 Module Autoloader Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 893 199.2 Non-Standard / Explicit Module Paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 894 199.3 Packaging Modules with Phar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 895 200Best Practices when Creating Modules 897 200.1 Keep the init() and onBootstrap() methods lightweight . . . . . . . . . . . . . . . . . . . . 897 200.2 Do not perform writes within a module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 897 200.3 Utilize a vendor prefix for module names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 897 200.4 Utilize a module prefix for service names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 898 201Introduction to the MVC Layer 899 201.1 Basic Application Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 899 201.2 Basic Module Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 900 201.3 Bootstrapping an Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 902 201.4 Bootstrapping a Modular Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 904 201.5 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 905 202Quick Start 907 202.1 Install the Zend Skeleton Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 907 202.2 Create a New Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 908 202.3 Update the Module Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 908 xix
  • 22. 202.4 Create a Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 909 202.5 Create a View Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 910 202.6 Create a Route . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 910 202.7 Tell the Application About our Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 912 202.8 Test it Out! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 912 203Default Services 915 203.1 Theory of Operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 915 203.2 ServiceManager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 915 203.3 Abstract Factories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 920 203.4 Plugin Managers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 922 203.5 ViewManager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 923 203.6 Application Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 924 203.7 Default Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 925 204Routing 929 204.1 Router Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 931 204.2 HTTP Route Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 931 204.3 HTTP Routing Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 937 204.4 Console Route Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 940 205The MvcEvent 941 205.1 Order of events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 942 205.2 MvcEvent::EVENT_BOOTSTRAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 942 205.3 MvcEvent::EVENT_ROUTE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 943 205.4 MvcEvent::EVENT_DISPATCH . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 943 205.5 MvcEvent::EVENT_DISPATCH_ERROR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 945 205.6 MvcEvent::EVENT_RENDER . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 946 205.7 MvcEvent::EVENT_RENDER_ERROR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 947 205.8 MvcEvent::EVENT_FINISH . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 948 206The SendResponseEvent 949 206.1 Listeners . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 949 206.2 Triggerers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 949 207Available Controllers 951 207.1 Common Interfaces Used With Controllers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 951 207.2 The AbstractActionController . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 953 207.3 The AbstractRestfulController . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 954 208Controller Plugins 957 208.1 AcceptableViewModelSelector Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 957 208.2 FlashMessenger Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 958 208.3 Forward Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 960 208.4 Identity Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 961 208.5 Layout Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 962 208.6 Params Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 962 208.7 Post/Redirect/Get Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 963 208.8 File Post/Redirect/Get Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 963 208.9 Redirect Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 964 208.10Url Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 965 209Examples 967 209.1 Controllers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 967 209.2 Bootstrapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 968 xx
  • 23. 210Introduction to ZendNavigation 969 210.1 Pages and Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 969 210.2 View Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 969 211Quick Start 971 212Pages 973 212.1 Common page features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 973 212.2 ZendNavigationPageMvc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 975 212.3 ZendNavigationPageUri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 978 212.4 Creating custom page types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 978 212.5 Creating pages using the page factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 979 213Containers 983 213.1 Creating containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 983 213.2 Adding pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 989 213.3 Removing pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 990 213.4 Finding pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 991 213.5 Iterating containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 993 213.6 Other operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 993 214View Helpers 997 214.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 997 214.2 Translation of labels and titles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 998 214.3 Integration with ACL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 998 214.4 Navigation setup used in examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 999 215View Helper - Breadcrumbs 1005 215.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1005 215.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1005 215.3 Specifying indentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1006 215.4 Customize output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1006 215.5 Rendering using a partial view script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1006 216View Helper - Links 1009 216.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1009 216.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1011 217View Helper - Menu 1013 217.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1013 217.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1014 217.3 Calling renderMenu() directly . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1015 217.4 Rendering the deepest active menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1016 217.5 Rendering with maximum depth . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1016 217.6 Rendering with minimum depth . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1017 217.7 Rendering only the active branch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1018 217.8 Rendering only the active branch with minimum depth . . . . . . . . . . . . . . . . . . . . . . . . . 1019 217.9 Rendering only the active branch with maximum depth . . . . . . . . . . . . . . . . . . . . . . . . . 1019 217.10Rendering only the active branch with maximum depth and no parents . . . . . . . . . . . . . . . . . 1020 217.11Rendering a custom menu using a partial view script . . . . . . . . . . . . . . . . . . . . . . . . . . 1020 218View Helper - Sitemap 1023 218.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1023 218.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1024 218.3 Rendering using no ACL role . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1025 xxi
  • 24. 218.4 Rendering using a maximum depth . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1026 219View Helper - Navigation Proxy 1029 219.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1029 219.2 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1029 220Introduction to ZendPaginator 1031 221Usage 1033 221.1 Paginating data collections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1033 221.2 The DbSelect adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1034 221.3 Rendering pages with view scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1035 222Configuration 1041 223Advanced usage 1043 223.1 Custom data source adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1043 223.2 Custom scrolling styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1043 223.3 Caching features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1044 224Introduction to ZendPermissionsAcl 1047 224.1 Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1047 224.2 Roles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1048 224.3 Creating the Access Control List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1049 224.4 Registering Roles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1049 224.5 Defining Access Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1050 224.6 Querying an ACL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1051 225Refining Access Controls 1053 225.1 Precise Access Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1053 225.2 Removing Access Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1054 226Advanced Usage 1057 226.1 Storing ACL Data for Persistence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1057 226.2 Writing Conditional ACL Rules with Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1057 227Introduction to ZendPermissionsRbac 1059 227.1 Roles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1059 227.2 Permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1059 227.3 Dynamic Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1059 228Methods 1061 229Examples 1063 229.1 Roles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1063 229.2 Permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1064 229.3 Dynamic Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1064 230Progress Bars 1067 230.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1067 230.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1067 230.3 Persistent Progress . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1067 230.4 Standard Adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1068 231File Upload Handlers 1071 231.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1071 xxii
  • 25. 231.2 Methods of Reporting Progress . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1071 231.3 Standard Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1072 232Introduction to ZendSerializer 1075 232.1 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1075 232.2 Basic configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1076 232.3 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1076 233ZendSerializerAdapter 1079 233.1 The PhpSerialize Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1079 233.2 The IgBinary Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1079 233.3 The Wddx Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1079 233.4 The Json Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1080 233.5 The PythonPickle Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1080 233.6 The PhpCode Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1081 234Introduction to ZendServer 1083 235ZendServerReflection 1085 235.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1085 235.2 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1085 236ZendServiceManager 1087 237ZendServiceManager Quick Start 1091 237.1 Using Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1091 237.2 Modules as Service Providers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1092 237.3 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1092 238Delegator service factories 1097 238.1 Delegator factory signature . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1097 238.2 A Delegator factory use case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1097 239Lazy Services 1101 239.1 Use cases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1101 239.2 Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1101 239.3 Practical example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1101 239.4 Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1103 240Session Config 1105 240.1 Standard Config . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1105 240.2 Session Config . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1106 240.3 Custom Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1107 241Session Container 1109 241.1 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1109 241.2 Setting the Default Session Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1109 242Session Manager 1111 242.1 Initializing the Session Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1111 242.2 Session Compatibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1113 243Session Save Handlers 1115 243.1 Cache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1115 243.2 DbTableGateway . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1115 243.3 MongoDB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1116 xxiii
  • 26. 243.4 Custom Save Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1117 244Session Storage 1119 244.1 Array Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1119 244.2 Session Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1119 244.3 Session Array Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1120 244.4 Custom Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1120 245Session Validators 1121 245.1 Http User Agent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1121 245.2 Remote Addr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1121 245.3 Custom Validators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1122 246ZendSoapServer 1123 246.1 ZendSoapServer constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1123 246.2 Methods to define Web Service API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1124 246.3 Request and response objects handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1125 246.4 Document/Literal WSDL Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1127 247ZendSoapClient 1129 247.1 ZendSoapClient Constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1129 247.2 Performing SOAP Requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1130 248WSDL Accessor 1133 248.1 ZendSoapWsdl constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1133 248.2 addMessage() method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1133 248.3 addPortType() method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1134 248.4 addPortOperation() method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1134 248.5 addBinding() method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1134 248.6 addBindingOperation() method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1135 248.7 addSoapBinding() method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1135 248.8 addSoapOperation() method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1135 248.9 addService() method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1135 248.10Type mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1136 248.11addDocumentation() method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1137 248.12Get finalized WSDL document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1138 249AutoDiscovery 1139 249.1 AutoDiscovery Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1139 249.2 Class autodiscovering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1140 249.3 Functions autodiscovering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1141 249.4 Autodiscovering Datatypes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1141 249.5 WSDL Binding Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1141 250ZendStdlibHydrator 1143 250.1 HydratorInterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1143 250.2 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1143 250.3 Available Implementations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1144 251ZendStdlibHydratorFilter 1145 251.1 Filter implementations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1145 251.2 Remove filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1146 251.3 Add filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1146 251.4 Use the composite for complex filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1147 251.5 Using the provider interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1148 xxiv
  • 27. 252ZendStdlibHydratorStrategy 1151 252.1 Adding strategies to the hydrators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1151 252.2 Available implementations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1152 252.3 Writing custom strategies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1152 253AggregateHydrator 1155 253.1 Installation requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1155 253.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1155 253.3 Advanced use cases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1156 254Introduction to ZendTag 1159 255Creating tag clouds with ZendTagCloud 1161 255.1 Decorators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1163 256Introduction to ZendTest 1167 257Unit testing with PHPUnit 1169 258Setup your TestCase 1171 259Testing your Controllers and MVC Applications 1173 260Assertions 1175 261Request Assertions 1177 262CSS Selector Assertions 1179 263XPath Assertions 1181 264Redirect Assertions 1183 265Response Header Assertions 1185 266ZendTextFiglet 1187 266.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1187 266.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1188 267ZendTextTable 1189 267.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1189 267.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1190 268ZendUri 1191 268.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1191 268.2 Creating a New URI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1191 268.3 Manipulating an Existing URI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1192 268.4 Common Instance Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1192 269Introduction to ZendValidator 1197 269.1 What is a validator? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1197 269.2 Basic usage of validators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1197 269.3 Customizing messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1198 269.4 Translating messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1199 270Standard Validation Classes 1201 270.1 Alnum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1201 xxv
  • 28. 271Alpha 1203 271.1 Supported options for ZendI18nValidatorAlpha . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1203 271.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1203 271.3 Using whitespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1203 271.4 Using different languages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1204 272Barcode 1205 272.1 Supported options for ZendValidatorBarcode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1207 272.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1208 272.3 Optional checksum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1208 272.4 Writing custom adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1208 273Between 1211 273.1 Supported options for ZendValidatorBetween . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1211 273.2 Default behaviour for ZendValidatorBetween . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1211 273.3 Validation exclusive the border values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1211 274Callback 1213 274.1 Supported options for ZendValidatorCallback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1213 274.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1213 274.3 Usage with closures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1213 274.4 Usage with class-based callbacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1214 274.5 Adding options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1215 275CreditCard 1217 275.1 Supported options for ZendValidatorCreditCard . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1218 275.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1218 275.3 Accepting defined credit cards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1218 275.4 Validation by using foreign APIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1219 275.5 Ccnum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1220 276Date 1221 276.1 Supported options for ZendValidatorDate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1221 276.2 Default date validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1221 276.3 Self defined date validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1221 277DbRecordExists and DbNoRecordExists 1223 277.1 Supported options for ZendValidatorDb* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1223 277.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1223 277.3 Excluding records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1224 277.4 Database Schemas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1225 277.5 Using a Select object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1225 278Digits 1227 278.1 Supported options for ZendValidatorDigits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1227 278.2 Validating digits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1227 279EmailAddress 1229 279.1 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1229 279.2 Options for validating Email Addresses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1229 279.3 Complex local parts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1230 279.4 Validating only the local part . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1230 279.5 Validating different types of hostnames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1230 279.6 Checking if the hostname actually accepts email . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1230 279.7 Validating International Domains Names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1231 xxvi
  • 29. 279.8 Validating Top Level Domains . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1232 279.9 Setting messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1232 280File Validation Classes 1233 280.1 Crc32 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1233 280.2 ExcludeExtension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1234 280.3 ExcludeMimeType . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1234 280.4 Exists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1234 280.5 Extension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1235 280.6 Hash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1236 280.7 ImageSize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1237 280.8 IsCompressed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1238 280.9 IsImage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1238 280.10Md5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1238 280.11MimeType . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1239 280.12NotExists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1240 280.13Sha1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1241 280.14Size . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1242 280.15UploadFile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1243 280.16WordCount . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1243 281Float 1245 281.1 Supported options for ZendI18nValidatorFloat . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1245 281.2 Simple float validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1245 281.3 Localized float validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1245 282GreaterThan 1247 282.1 Supported options for ZendValidatorGreaterThan . . . . . . . . . . . . . . . . . . . . . . . . . . . 1247 282.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1247 282.3 Validation inclusive the border value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1247 283Hex 1249 283.1 Supported options for ZendValidatorHex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1249 284Hostname 1251 284.1 Supported options for ZendValidatorHostname . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1251 284.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1251 284.3 Validating different types of hostnames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1251 284.4 Validating International Domains Names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1252 284.5 Validating Top Level Domains . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1253 285Iban 1255 285.1 Supported options for ZendValidatorIban . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1255 285.2 IBAN validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1255 286Identical 1257 286.1 Supported options for ZendValidatorIdentical . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1257 286.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1257 286.3 Identical objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1257 286.4 Form elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1258 286.5 Strict validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1260 286.6 Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1260 287InArray 1261 287.1 Supported options for ZendValidatorInArray . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1261 xxvii
  • 30. 287.2 Simple array validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1261 287.3 Array validation modes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1262 287.4 Recursive array validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1263 288Ip 1265 288.1 Supported options for ZendValidatorIp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1265 288.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1265 288.3 Validate IPv4 or IPV6 alone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1266 289Isbn 1267 289.1 Supported options for ZendValidatorIsbn . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1267 289.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1267 289.3 Setting an explicit ISBN validation type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1267 289.4 Specifying a separator restriction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1268 290LessThan 1269 290.1 Supported options for ZendValidatorLessThan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1269 290.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1269 290.3 Validation inclusive the border value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1269 291NotEmpty 1271 291.1 Supported options for ZendValidatorNotEmpty . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1271 291.2 Default behaviour for ZendValidatorNotEmpty . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1271 291.3 Changing behaviour for ZendValidatorNotEmpty . . . . . . . . . . . . . . . . . . . . . . . . . . . 1271 292PostCode 1273 292.1 Constructor options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1274 292.2 Supported options for ZendValidatorPostCode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1274 293Regex 1275 293.1 Supported options for ZendValidatorRegex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1275 293.2 Validation with ZendValidatorRegex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1275 293.3 Pattern handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1275 294Sitemap Validators 1277 294.1 SitemapChangefreq . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1277 294.2 SitemapLastmod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1277 294.3 SitemapLoc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1277 294.4 SitemapPriority . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1278 294.5 Supported options for ZendValidatorSitemap_* . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1278 295Step 1279 295.1 Supported options for ZendValidatorStep . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1279 295.2 Basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1279 295.3 Using floating-point values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1279 296StringLength 1281 296.1 Supported options for ZendValidatorStringLength . . . . . . . . . . . . . . . . . . . . . . . . . . . 1281 296.2 Default behaviour for ZendValidatorStringLength . . . . . . . . . . . . . . . . . . . . . . . . . . . 1281 296.3 Limiting the maximum allowed length of a string . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1281 296.4 Limiting the minimal required length of a string . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1282 296.5 Limiting a string on both sides . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1282 296.6 Encoding of values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1282 297File Validation Classes 1285 297.1 Crc32 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1285 xxviii
  • 31. 297.2 ExcludeExtension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1286 297.3 ExcludeMimeType . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1286 297.4 Exists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1286 297.5 Extension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1287 297.6 Hash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1288 297.7 ImageSize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1289 297.8 IsCompressed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1290 297.9 IsImage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1290 297.10Md5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1290 297.11MimeType . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1291 297.12NotExists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1292 297.13Sha1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1293 297.14Size . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1294 297.15UploadFile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1295 297.16WordCount . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1295 298Validator Chains 1297 298.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1297 299Writing Validators 1299 299.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1299 299.2 Creating a Simple Validation Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1299 299.3 Writing a Validation Class having Dependent Conditions . . . . . . . . . . . . . . . . . . . . . . . . 1300 299.4 Validation with Independent Conditions, Multiple Reasons for Failure . . . . . . . . . . . . . . . . . 1301 300Validation Messages 1303 300.1 Using pre-translated validation messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1303 300.2 Limit the size of a validation message . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1304 301Getting the Zend Framework Version 1305 301.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1305 301.2 Example of the compareVersion() Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1305 301.3 Example of the getLatest() Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1305 302ZendView Quick Start 1307 302.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1307 302.2 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1307 303The PhpRenderer 1321 303.1 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1321 303.2 Options and Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1325 303.3 Additional Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1325 304PhpRenderer View Scripts 1327 304.1 Escaping Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1328 305The ViewEvent 1329 305.1 Order of events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1329 305.2 ViewEvent::EVENT_RENDERER . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1330 305.3 ViewEvent::EVENT_RENDERER_POST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1331 305.4 ViewEvent::EVENT_RESPONSE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1331 306View Helpers 1333 306.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1333 306.2 Included Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1334 xxix
  • 32. 307View Helper - BasePath 1335 307.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1335 307.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1335 308View Helper - Cycle 1337 308.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1337 308.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1337 308.3 Working with two or more cycles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1338 309View Helper - Doctype 1339 309.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1339 309.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1339 309.3 Retrieving the Doctype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1340 309.4 Choosing a Doctype to Use with the Open Graph Protocol . . . . . . . . . . . . . . . . . . . . . . . 1340 309.5 Zend MVC View Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1341 310FlashMessenger Helper 1343 310.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1343 310.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1343 310.3 CSS Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1343 310.4 HTML Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1344 310.5 Sample Modification for Twitter Bootstrap 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1344 310.6 Alternative Configuration of the ViewHelper Layout . . . . . . . . . . . . . . . . . . . . . . . . . . 1345 311Gravatar Helper 1347 311.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1347 311.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1347 311.3 Custom Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1347 312View Helper - HeadLink 1349 312.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1349 312.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1349 313View Helper - HeadMeta 1351 313.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1351 313.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1352 313.3 Usage with XHTML1_RDFA doctype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1352 314View Helper - HeadScript 1355 314.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1355 314.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1357 314.3 Capturing Scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1357 315View Helper - HeadStyle 1359 315.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1359 315.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1362 315.3 Capturing Style Declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1362 316View Helper - HeadTitle 1363 316.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1363 316.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1363 317View Helper - HtmlList 1365 317.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1365 317.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1365 xxx
  • 33. 318View Helper - HTML Object 1369 318.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1369 318.2 Flash helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1369 318.3 Customizing the object by passing additional arguments . . . . . . . . . . . . . . . . . . . . . . . . 1369 319View Helper - Identity 1371 319.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1371 319.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1371 319.3 Using with ServiceManager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1371 320View Helper - InlineScript 1373 320.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1373 320.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1373 320.3 Capturing Scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1374 321View Helper - JSON 1375 321.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1375 321.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1375 322View Helper - Partial 1377 322.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1377 322.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1377 322.3 Using PartialLoop to Render Iterable Models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1378 323View Helper - Placeholder 1381 323.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1381 323.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1381 323.3 Aggregate Content . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1381 323.4 Capture Content . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1382 323.5 Concrete Implementations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1383 324View Helper - URL 1385 324.1 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1385 325Advanced usage of helpers 1387 325.1 Registering Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1387 325.2 Writing Custom Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1388 325.3 Registering Concrete Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1390 326Introduction to ZendXmlRpc 1391 326.1 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1391 327ZendXmlRpcClient 1393 327.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1393 327.2 Method Calls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1393 327.3 Types and Conversions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1394 327.4 Server Proxy Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1395 327.5 Error Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1396 327.6 Server Introspection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1397 327.7 From Request to Response . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1398 327.8 HTTP Client and Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1398 328ZendXmlRpcServer 1399 328.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1399 328.2 Basic Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1399 328.3 Server Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1399 xxxi
  • 34. 328.4 Anatomy of a webservice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1400 328.5 Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1400 328.6 Utilizing Namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1401 328.7 Custom Request Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1401 328.8 Custom Responses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1402 328.9 Handling Exceptions via Faults . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1402 328.10Caching Server Definitions Between Requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1402 328.11Usage Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1403 328.12Performance optimization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1407 329ZendServiceAkismet 1409 329.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1409 329.2 Verify an API key . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1409 329.3 Check for spam . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1410 329.4 Submitting known spam . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1410 329.5 Submitting false positives (ham) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1411 329.6 Zend-specific Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1411 330ZendServiceAmazon 1413 330.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1413 330.2 Country Codes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1414 330.3 Looking up a Specific Amazon Item by ASIN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1414 330.4 Performing Amazon Item Searches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1415 330.5 Using the Alternative Query API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1415 330.6 ZendServiceAmazon Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1416 331ZendServiceAmazonS3 1421 331.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1421 331.2 Registering with Amazon S3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1421 331.3 API Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1421 331.4 Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1421 331.5 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1422 331.6 Bucket operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1422 331.7 Object operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1423 331.8 Data Streaming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1424 331.9 Stream wrapper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1425 332ZendServiceAmazonSqs 1427 332.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1427 332.2 Registering with Amazon SQS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1427 332.3 API Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1427 332.4 Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1427 332.5 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1428 332.6 Queue operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1428 332.7 Message operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1429 333ZendServiceAmazonEc2 1431 333.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1431 333.2 What is Amazon Ec2? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1431 333.3 Static Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1431 334ZendServiceAmazonEc2: CloudWatch Monitoring 1433 334.1 CloudWatch Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1433 335ZendServiceAmazonEc2: Elastic Block Storage (EBS) 1435 xxxii
  • 35. 335.1 Create EBS Volumes and Snapshots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1435 335.2 Describing EBS Volumes and Snapshots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1436 335.3 Attach and Detaching Volumes from Instances . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1437 335.4 Deleting EBS Volumes and Snapshots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1437 336ZendServiceAmazonEc2: Elastic IP Addresses 1439 336.1 Allocating a new Elastic IP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1439 337ZendServiceAmazonEc2: Instances 1441 337.1 Instance Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1441 337.2 Running Amazon EC2 Instances . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1442 337.3 Amazon Instance Utilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1443 338ZendServiceAmazonEc2: Keypairs 1445 338.1 Creating a new Amazon Keypair . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1445 338.2 Deleting an Amazon Keypair . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1445 338.3 Describe an Amazon Keypair . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1445 339ZendServiceAmazonEc2: Regions and Availability Zones 1447 339.1 Amazon EC2 Regions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1447 339.2 Amazon EC2 Availability Zones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1447 340ZendServiceAmazonEc2: Reserved Instances 1449 340.1 How Reserved Instances are Applied . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1449 340.2 Reserved Instances Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1449 341ZendServiceAmazonEc2: Security Groups 1451 341.1 Security Group Maintenance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1451 341.2 Authorizing Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1452 341.3 Revoking Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1453 342ZendServiceAmazonEc2: Windows Instances 1455 342.1 Windows Instances Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1456 343ZendServiceAppleApns 1457 343.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1457 343.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1457 343.3 Feedback Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1459 344ZendServiceAudioscrobbler 1461 344.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1461 344.2 Users . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1461 344.3 Artists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1463 344.4 Tracks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1463 344.5 Tags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1464 344.6 Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1464 344.7 Forums . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1464 345ZendServiceDelicious 1467 345.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1467 345.2 Retrieving posts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1467 345.3 ZendServiceDeliciousPostList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1468 345.4 Editing posts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1469 345.5 Deleting posts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1469 345.6 Adding new posts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1470 345.7 Tags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1470 xxxiii
  • 36. 345.8 Bundles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1471 345.9 Public data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1471 345.10HTTP client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1472 346ZendServiceDeveloperGarden 1473 346.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1473 346.2 BaseUserService . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1474 346.3 IP Location . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1475 346.4 Local Search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1476 346.5 Send SMS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1476 346.6 SMS Validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1477 346.7 Voice Call . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1477 346.8 ConferenceCall . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1478 346.9 Performance and Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1480 347ZendServiceFlickr 1481 347.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1481 347.2 Finding Flickr Users’ Photos and Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1481 347.3 Finding photos From a Group Pool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1482 347.4 Retrieving Flickr Image Details . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1482 347.5 ZendServiceFlickr Result Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1482 348ZendServiceGoogleGcm 1485 348.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1485 348.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1485 349ZendServiceLiveDocx 1487 349.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1487 349.2 ZendServiceLiveDocxMailMerge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1489 350ZendServiceRackspace 1501 350.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1501 350.2 Registering with Rackspace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1501 350.3 Cloud Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1501 350.4 Cloud Servers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1502 350.5 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1502 351ZendServiceRackspaceServers 1505 351.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1505 351.2 Terminology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1505 351.3 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1506 351.4 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1507 351.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1511 352ZendServiceRackspaceFiles 1513 352.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1513 352.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1513 352.3 Available Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1515 352.4 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1518 353ZendServiceReCaptcha 1521 353.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1521 353.2 Simplest use . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1521 353.3 Hiding email addresses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1522 xxxiv
  • 37. 354ZendServiceSlideShare 1525 354.1 Getting Started with ZendServiceSlideShare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1525 354.2 The SlideShow object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1525 354.3 Retrieving a single slide show . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1528 354.4 Retrieving Groups of Slide Shows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1528 354.5 ZendServiceSlideShare Caching policies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1529 354.6 Changing the behavior of the HTTP Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1529 355ZendServiceStrikeIron 1531 355.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1531 355.2 Registering with StrikeIron . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1532 355.3 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1532 355.4 Making Your First Query . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1532 355.5 Examining Results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1533 355.6 Handling Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1534 355.7 Checking Your Subscription . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1534 356ZendServiceStrikeIron: Bundled Services 1537 356.1 ZIP Code Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1537 356.2 U.S. Address Verification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1538 356.3 Sales & Use Tax Basic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1538 357ZendServiceStrikeIron: Advanced Uses 1541 357.1 Using Services by WSDL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1541 357.2 Viewing SOAP Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1541 358ZendServiceTechnorati 1543 358.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1543 358.2 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1543 358.3 Making Your First Query . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1543 358.4 Consuming Results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1544 358.5 Handling Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1546 358.6 Checking Your API Key Daily Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1546 358.7 Available Technorati Queries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1546 358.8 ZendServiceTechnorati Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1550 359ZendServiceTwitter 1555 359.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1555 359.2 Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1555 359.3 Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1557 359.4 Account Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1558 359.5 Application Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1558 359.6 Blocking Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1559 359.7 Direct Message Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1559 359.8 Favorites Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1560 359.9 Friendship Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1561 359.10Search Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1561 359.11Status Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1562 359.12User Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1564 360ZendServiceWindowsAzure 1565 360.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1565 360.2 Installing the Windows Azure SDK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1565 360.3 API Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1565 360.4 Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1565 xxxv
  • 38. 360.5 Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1566 361ZendServiceWindowsAzureStorageBlob 1567 361.1 API Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1567 361.2 Root container . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1569 361.3 Blob storage stream wrapper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1569 361.4 Shared Access Signature . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1570 362ZendServiceWindowsAzureStorageTable 1573 362.1 Operations on tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1573 362.2 Operations on entities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1574 362.3 Table storage session handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1580 363ZendServiceWindowsAzureStorageQueue 1583 363.1 API Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1583 364Copyright Information 1587 365Introduction to Zend Framework 2 1589 366User Guide 1591 367Getting Started With Zend Studio 10 & Zend Server 6 1593 368Zend Framework Tool (ZFTool) 1595 369Learning Zend Framework 2 1597 370Migration 1599 371Zend Framework 2 Reference 1601 371.1 ZendAuthentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1601 371.2 ZendBarcode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1601 371.3 ZendCache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1601 371.4 ZendCaptcha . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1602 371.5 ZendCodeGenerator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1602 371.6 ZendConfig . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1602 371.7 ZendConsole . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1602 371.8 ZendConsoleGetopt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1602 371.9 ZendCrypt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1603 371.10ZendDb . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1603 371.11ZendDebug . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1603 371.12ZendDi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1603 371.13ZendDom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1603 371.14ZendEscaper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1604 371.15ZendEventManager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1604 371.16ZendException . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1604 371.17ZendFeed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1604 371.18ZendFile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1604 371.19ZendFilter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1605 371.20ZendForm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1605 371.21ZendHttp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1605 371.22ZendI18n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1605 371.23ZendInputFilter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1606 371.24ZendJson . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1606 371.25ZendLdap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1606 xxxvi
  • 39. 371.26ZendLoader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1606 371.27ZendLog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1607 371.28ZendMail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1607 371.29ZendMath . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1607 371.30ZendMemory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1607 371.31ZendMime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1607 371.32ZendModuleManager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1607 371.33ZendMvc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1608 371.34ZendNavigation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1608 371.35ZendPaginator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1608 371.36ZendPermissionsAcl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1608 371.37ZendPermissionsRbac . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1609 371.38ZendProgressBar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1609 371.39ZendSerializer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1609 371.40ZendServer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1609 371.41ZendServiceManager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1609 371.42ZendSession . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1609 371.43ZendSoap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1610 371.44ZendStdlib . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1610 371.45ZendTag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1610 371.46ZendTest . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1610 371.47ZendText . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1610 371.48ZendUri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1610 371.49ZendValidator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1610 371.50ZendVersion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1611 371.51ZendView . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1611 371.52ZendXmlRpc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1612 372Services for Zend Framework 2 Reference 1613 372.1 ZendServiceAkismet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1613 372.2 ZendServiceAmazon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1613 372.3 ZendServiceAppleApns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1613 372.4 ZendServiceAudioscrobbler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1613 372.5 ZendServiceDelicious . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1614 372.6 ZendServiceDeveloperGarden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1614 372.7 ZendServiceFlickr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1614 372.8 ZendServiceGoogleGcm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1614 372.9 ZendServiceLiveDocx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1614 372.10ZendServiceRackspace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1614 372.11ZendServiceReCaptcha . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1614 372.12ZendServiceSlideShare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1614 372.13ZendServiceStrikeIron . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1615 372.14ZendServiceTechnorati . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1615 372.15ZendServiceTwitter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1615 372.16ZendServiceWindowsAzure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1615 373Copyright 1617 374Indices and tables 1619 xxxvii
  • 41. 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 exten- sible 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, vali- dation, 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
  • 42. Zend Framework 2 Documentation, Release 2.3.1dev 2 Chapter 1. Overview
  • 43. 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 en- vironment 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
  • 44. Zend Framework 2 Documentation, Release 2.3.1dev 4 Chapter 2. Installation
  • 45. 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.23 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 distribution’s 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. Note: Alternatively, if you are using PHP 5.4+ you may use the built-in web server instead of Apache for development. 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 Description List of albums 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. Add new album This page will provide a form for adding a new album. Edit album This page will provide a form for editing an album. Delete album This page will confirm that we want to delete an album and then delete it. 5
  • 46. Zend Framework 2 Documentation, Release 2.3.1dev We will also need to store our data into a database. We will only need one table with these fields in it: Field name Type Null? Notes id integer No Primary key, auto-increment artist varchar(100) No title varchar(100) No 6 Chapter 3. Getting Started with Zend Framework 2
  • 47. 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 php composer.phar create-project --repository-url="https://packages.zendframework.com" zendframework 2 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 php composer.phar self-update 2 php composer.phar install 3 php composer.phar update from the zf2-tutorial folder. This takes a while. You should see an output like: 1 Installing dependencies from lock file 2 - Installing zendframework/zendframework (dev-master) 3 Cloning 18c8e223f070deb07c17543ed938b54542aa0ed8 4 5 Generating autoload files Note: If you see this message: 1 [RuntimeException] 2 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: 1 php composer.phar install 2 php composer.phar update run instead: 7
  • 48. Zend Framework 2 Documentation, Release 2.3.1dev 1 COMPOSER_PROCESS_TIMEOUT=5000 php composer.phar install 2 COMPOSER_PROCESS_TIMEOUT=5000 php composer.phar update Note: For windows users with wamp: 1. Install composer for windows Check composer is properly installed by running 1 composer 2. Install git for windows. Also need to add git path in windows environment variable Check git is properly installed by running 1 git 3. Now install zf2 using command 1 composer create-project --repository-url="https://packages.zendframework.com" -s dev zendframewo We can now move on to the web server setup. 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://zf2-tutorial.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 <VirtualHost *:80> 2 ServerName zf2-tutorial.localhost 3 DocumentRoot /path/to/zf2-tutorial/public 4 SetEnv APPLICATION_ENV "development" 5 <Directory /path/to/zf2-tutorial/public> 6 DirectoryIndex index.php 7 AllowOverride All 8 Order allow,deny 9 Allow from all 10 </Directory> 11 </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://zf2-tutorial.localhost. 1 127.0.0.1 zf2-tutorial.localhost localhost Restart Apache. If you’ve done it correctly, it should look something like this: 8 Chapter 4. Getting started: A skeleton application
  • 49. Zend Framework 2 Documentation, Release 2.3.1dev 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 RewriteCond %{REQUEST_FILENAME} !-f 2 RewriteRule ^ index.php [NC,L] You now have a working skeleton application and we can start adding the specifics for our application. 4.2 Using the Built-in PHP CLI Server Alternatively — if you are using PHP 5.4 or above — you can use the built-in CLI server (cli-server). To do this, you just start the server in the root directory: 1 php -S 0.0.0.0:8080 -t public/ public/index.php This will make the website available on port 8080 on all network interfaces, using public/index.php to handle routing. This means the site is accessible via http://localhost:8080 or http://<your-local-IP>:8080. If you’ve done it right, you should see the same result as with Apache above. To test that your routing is working, navigate to http://localhost:8080/1234 and you should see the same error page as with Apache above. Note: The built-in CLI server is for development only. 4.3 Error reporting Optionally, when using Apache, you can use the APPLICATION_ENV setting in your VirtualHost to let PHP output all its errors to the browser. This can be useful during the development of your application. Edit index.php from the zf2-tutorial/public/ directory and change it to the following: 1 <?php 2 3 /** 4 * Display all errors when APPLICATION_ENV is development. 5 */ 6 if ($_SERVER[’APPLICATION_ENV’] == ’development’) { 7 error_reporting(E_ALL); 8 ini_set("display_errors", 1); 9 } 10 11 /** 12 * This makes our life easier when dealing with paths. Everything is relative 13 * to the application root now. 14 */ 15 chdir(dirname(__DIR__)); 16 17 // Decline static file requests back to the PHP built-in webserver 18 if (php_sapi_name() === ’cli-server’ && is_file(__DIR__ . parse_url($_SERVER[’REQUEST_URI’], PHP_URL 19 return false; 20 } 4.2. Using the Built-in PHP CLI Server 9
  • 50. Zend Framework 2 Documentation, Release 2.3.1dev 21 22 // Setup autoloading 23 require ’init_autoloader.php’; 24 25 // Run the application! 26 ZendMvcApplication::init(require ’config/application.config.php’)->run(); 10 Chapter 4. Getting started: A skeleton application
  • 51. CHAPTER 5 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 Description Home 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. Add new album This page will provide a form for adding a new album. Edit album This page will provide a form for editing an album. Delete album This page will confirm that we want to delete an album and then delete it. Before we set up our files, it’s 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 Controller Action Home AlbumController index Add new album AlbumController add Edit album AlbumController edit Delete album AlbumController delete The mapping of a URL to a particular action is done using routes that are defined in the module’s 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 return array( 2 ’controllers’ => array( 3 ’invokables’ => array( 4 ’AlbumControllerAlbum’ => ’AlbumControllerAlbumController’, 5 ), 6 ), 7 8 // The following section is new and should be added to your file 9 ’router’ => array( 10 ’routes’ => array( 11 ’album’ => array( 12 ’type’ => ’segment’, 13 ’options’ => array( 11
  • 52. Zend Framework 2 Documentation, Release 2.3.1dev 14 ’route’ => ’/album[/][:action][/:id]’, 15 ’constraints’ => array( 16 ’action’ => ’[a-zA-Z][a-zA-Z0-9_-]*’, 17 ’id’ => ’[0-9]+’, 18 ), 19 ’defaults’ => array( 20 ’controller’ => ’AlbumControllerAlbum’, 21 ’action’ => ’index’, 22 ), 23 ), 24 ), 25 ), 26 ), 27 28 ’view_manager’ => array( 29 ’template_path_stack’ => array( 30 ’album’ => __DIR__ . ’/../view’, 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 Page Action /album Home (list of albums) index /album/add Add new album add /album/edit/2 Edit album with an id of 2 edit /album/delete/4 Delete album with an id of 4 delete 12 Chapter 5. Routing and controllers
  • 53. CHAPTER 6 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 doesn’t 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. We’ll be using the stan- dard AbstractActionController, but if you’re intending to write a RESTful web service, AbstractRestfulController may be useful. Let’s go ahead and create our controller class AlbumController.php at zf2-tutorials/module/Album/src/Album/Controller : 1 namespace AlbumController; 2 3 use ZendMvcControllerAbstractActionController; 4 use ZendViewModelViewModel; 5 6 class AlbumController extends AbstractActionController 7 { 8 public function indexAction() 9 { 10 } 11 12 public function addAction() 13 { 14 } 15 16 public function editAction() 17 { 18 } 19 20 public function deleteAction() 21 { 22 } 23 } 13
  • 54. Zend Framework 2 Documentation, Release 2.3.1dev 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 won’t work yet until we set up the views. The URLs for each action are: URL Method called http://zf2-tutorial.localhost/album AlbumControllerAlbumController::indexAction http://zf2-tutorial.localhost/album/addAlbumControllerAlbumController::addAction http://zf2-tutorial.localhost/album/editAlbumControllerAlbumController::editAction http://zf2-tutorial.localhost/album/deleteAlbumControllerAlbumController::deleteAction We now have a working router and the actions are set up for each page of our application. It’s time to build the view and the model layer. 6.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 module’s 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. 14 Chapter 6. Create the controller
  • 55. CHAPTER 7 Database and models 7.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 application’s 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 PHP’s PDO driver, so create a database called zf2tutorial, and run these SQL statements to create the album table with some data in it. 1 CREATE TABLE album ( 2 id int(11) NOT NULL auto_increment, 3 artist varchar(100) NOT NULL, 4 title varchar(100) NOT NULL, 5 PRIMARY KEY (id) 6 ); 7 INSERT INTO album (artist, title) 8 VALUES (’The Military Wives’, ’In My Dreams’); 9 INSERT INTO album (artist, title) 10 VALUES (’Adele’, ’21’); 11 INSERT INTO album (artist, title) 12 VALUES (’Bruce Springsteen’, ’Wrecking Ball (Deluxe)’); 13 INSERT INTO album (artist, title) 14 VALUES (’Lana Del Rey’, ’Born To Die’); 15 INSERT INTO album (artist, title) 16 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. 7.2 The model files Zend Framework does not provide a ZendModel component because the model is your business logic and it’s 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 that load and save entities to the database. Another is to use an Object-relational mapping (ORM) technology, such as Doctrine or Propel. 15
  • 56. Zend Framework 2 Documentation, Release 2.3.1dev 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 sys- tems. There is also a temptation to put database access code into controller action methods as these are exposed by ZendDbTableGatewayAbstractTableGateway. Don’t do this! Let’s start by creating a file called Album.php under module/Album/src/Album/Model: 1 namespace AlbumModel; 2 3 class Album 4 { 5 public $id; 6 public $artist; 7 public $title; 8 9 public function exchangeArray($data) 10 { 11 $this->id = (!empty($data[’id’])) ? $data[’id’] : null; 12 $this->artist = (!empty($data[’artist’])) ? $data[’artist’] : null; 13 $this->title = (!empty($data[’title’])) ? $data[’title’] : null; 14 } 15 } Our Album entity object is a simple PHP class. In order to work with ZendDb’s TableGateway class, we need to implement the exchangeArray() method. This method simply copies the data from the passed in array to our entity’s 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 class AlbumTable 6 { 7 protected $tableGateway; 8 9 public function __construct(TableGateway $tableGateway) 10 { 11 $this->tableGateway = $tableGateway; 12 } 13 14 public function fetchAll() 15 { 16 $resultSet = $this->tableGateway->select(); 17 return $resultSet; 18 } 19 20 public function getAlbum($id) 21 { 22 $id = (int) $id; 23 $rowset = $this->tableGateway->select(array(’id’ => $id)); 24 $row = $rowset->current(); 25 if (!$row) { 26 throw new Exception("Could not find row $id"); 27 } 28 return $row; 16 Chapter 7. Database and models
  • 57. Zend Framework 2 Documentation, Release 2.3.1dev 29 } 30 31 public function saveAlbum(Album $album) 32 { 33 $data = array( 34 ’artist’ => $album->artist, 35 ’title’ => $album->title, 36 ); 37 38 $id = (int) $album->id; 39 if ($id == 0) { 40 $this->tableGateway->insert($data); 41 } else { 42 if ($this->getAlbum($id)) { 43 $this->tableGateway->update($data, array(’id’ => $id)); 44 } else { 45 throw new Exception(’Album id does not exist’); 46 } 47 } 48 } 49 50 public function deleteAlbum($id) 51 { 52 $this->tableGateway->delete(array(’id’ => (int) $id)); 53 } 54 } There’s 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() re- trieves 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. 7.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. We’ll 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 // Add these import statements: 4 use AlbumModelAlbum; 5 use AlbumModelAlbumTable; 6 use ZendDbResultSetResultSet; 7 use ZendDbTableGatewayTableGateway; 8 7.3. Using ServiceManager to configure the table gateway and inject into the AlbumTable 17
  • 58. Zend Framework 2 Documentation, Release 2.3.1dev 9 class Module 10 { 11 // getAutoloaderConfig() and getConfig() methods here 12 13 // Add this method: 14 public function getServiceConfig() 15 { 16 return array( 17 ’factories’ => array( 18 ’AlbumModelAlbumTable’ => function($sm) { 19 $tableGateway = $sm->get(’AlbumTableGateway’); 20 $table = new AlbumTable($tableGateway); 21 return $table; 22 }, 23 ’AlbumTableGateway’ => function ($sm) { 24 $dbAdapter = $sm->get(’ZendDbAdapterAdapter’); 25 $resultSetPrototype = new ResultSet(); 26 $resultSetPrototype->setArrayObjectPrototype(new Album()); 27 return new TableGateway(’album’, $dbAdapter, null, $resultSetPrototype); 28 }, 29 ), 30 ); 31 } 32 } This method returns an array of factories that are all merged together by the ModuleManager be- fore passing them 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 cre- ation 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 2’s ModuleManager merges all the configuration from each module’s module.config.php file and then merges in the files in config/autoload (*.global.php and then *.local.php files). We’ll 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 return array( 2 ’db’ => array( 3 ’driver’ => ’Pdo’, 4 ’dsn’ => ’mysql:dbname=zf2tutorial;host=localhost’, 5 ’driver_options’ => array( 6 PDO::MYSQL_ATTR_INIT_COMMAND => ’SET NAMES ’UTF8’’ 7 ), 8 ), 9 ’service_manager’ => array( 10 ’factories’ => array( 11 ’ZendDbAdapterAdapter’ 12 => ’ZendDbAdapterAdapterServiceFactory’, 13 ), 14 ), 15 ); 18 Chapter 7. Database and models
  • 59. Zend Framework 2 Documentation, Release 2.3.1dev You should put your database credentials in config/autoload/local.php so that they are not in the git repos- itory (as local.php is ignored): 1 return array( 2 ’db’ => array( 3 ’username’ => ’YOUR USERNAME HERE’, 4 ’password’ => ’YOUR PASSWORD HERE’, 5 ), 6 ); 7.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 // module/Album/src/Album/Controller/AlbumController.php: 2 public function getAlbumTable() 3 { 4 if (!$this->albumTable) { 5 $sm = $this->getServiceLocator(); 6 $this->albumTable = $sm->get(’AlbumModelAlbumTable’); 7 } 8 return $this->albumTable; 9 } 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(). 7.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 AlbumController’s indexAction() like this: 1 // module/Album/src/Album/Controller/AlbumController.php: 2 // ... 3 public function indexAction() 4 { 5 return new ViewModel(array( 6 ’albums’ => $this->getAlbumTable()->fetchAll(), 7 )); 8 } 9 // ... 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: 7.4. Back to the controller 19
  • 60. Zend Framework 2 Documentation, Release 2.3.1dev 1 <?php 2 // module/Album/view/album/album/index.phtml: 3 4 $title = ’My albums’; 5 $this->headTitle($title); 6 ?> 7 <h1><?php echo $this->escapeHtml($title); ?></h1> 8 <p> 9 <a href="<?php echo $this->url(’album’, array(’action’=>’add’));?>">Add new album</a> 10 </p> 11 12 <table class="table"> 13 <tr> 14 <th>Title</th> 15 <th>Artist</th> 16 <th>&nbsp;</th> 17 </tr> 18 <?php foreach ($albums as $album) : ?> 19 <tr> 20 <td><?php echo $this->escapeHtml($album->title);?></td> 21 <td><?php echo $this->escapeHtml($album->artist);?></td> 22 <td> 23 <a href="<?php echo $this->url(’album’, 24 array(’action’=>’edit’, ’id’ => $album->id));?>">Edit</a> 25 <a href="<?php echo $this->url(’album’, 26 array(’action’=>’delete’, ’id’ => $album->id));?>">Delete</a> 27 </td> 28 </tr> 29 <?php endforeach; ?> 30 </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 browser’s 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 don’t 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 album’s 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). If you open http://zf2-tutorial.localhost/album you should see this: 20 Chapter 7. Database and models
  • 61. CHAPTER 8 Styling and Translations We’ve picked up the SkeletonApplication’s styling, which is fine, but we need to change the title and remove the copyright message. The ZendSkeletonApplication is set up to use ZendI18n’s 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 gen- erated, 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 module’s layout.phtml view script: 1 // module/Application/view/layout/layout.phtml: 2 // Remove this line: 3 <p>&copy; 2005 - 2014 by Zend Technologies Ltd. <?php echo $this->translate(’All 4 rights reserved.’) ?></p> The page now looks ever so slightly better now! 21
  • 62. Zend Framework 2 Documentation, Release 2.3.1dev 22 Chapter 8. Styling and Translations
  • 63. CHAPTER 9 Forms and actions 9.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 class AlbumForm extends Form 6 { 7 public function __construct($name = null) 8 { 9 // we want to ignore the name passed 10 parent::__construct(’album’); 11 12 $this->add(array( 13 ’name’ => ’id’, 14 ’type’ => ’Hidden’, 15 )); 16 $this->add(array( 17 ’name’ => ’title’, 18 ’type’ => ’Text’, 19 ’options’ => array( 20 ’label’ => ’Title’, 21 ), 22 )); 23 $this->add(array( 24 ’name’ => ’artist’, 25 ’type’ => ’Text’, 26 ’options’ => array( 27 ’label’ => ’Artist’, 28 ), 29 )); 30 $this->add(array( 23
  • 64. Zend Framework 2 Documentation, Release 2.3.1dev 31 ’name’ => ’submit’, 32 ’type’ => ’Submit’, 33 ’attributes’ => array( 34 ’value’ => ’Go’, 35 ’id’ => ’submitbutton’, 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 parent’s constructor. 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 // Add these import statements 4 use ZendInputFilterInputFilter; 5 use ZendInputFilterInputFilterAwareInterface; 6 use ZendInputFilterInputFilterInterface; 7 8 class Album implements InputFilterAwareInterface 9 { 10 public $id; 11 public $artist; 12 public $title; 13 protected $inputFilter; // <-- Add this variable 14 15 public function exchangeArray($data) 16 { 17 $this->id = (isset($data[’id’])) ? $data[’id’] : null; 18 $this->artist = (isset($data[’artist’])) ? $data[’artist’] : null; 19 $this->title = (isset($data[’title’])) ? $data[’title’] : null; 20 } 21 22 // Add content to these methods: 23 public function setInputFilter(InputFilterInterface $inputFilter) 24 { 25 throw new Exception("Not used"); 26 } 27 28 public function getInputFilter() 29 { 30 if (!$this->inputFilter) { 31 $inputFilter = new InputFilter(); 32 33 $inputFilter->add(array( 34 ’name’ => ’id’, 35 ’required’ => true, 36 ’filters’ => array( 37 array(’name’ => ’Int’), 38 ), 39 )); 24 Chapter 9. Forms and actions
  • 65. Zend Framework 2 Documentation, Release 2.3.1dev 40 41 $inputFilter->add(array( 42 ’name’ => ’artist’, 43 ’required’ => true, 44 ’filters’ => array( 45 array(’name’ => ’StripTags’), 46 array(’name’ => ’StringTrim’), 47 ), 48 ’validators’ => array( 49 array( 50 ’name’ => ’StringLength’, 51 ’options’ => array( 52 ’encoding’ => ’UTF-8’, 53 ’min’ => 1, 54 ’max’ => 100, 55 ), 56 ), 57 ), 58 )); 59 60 $inputFilter->add(array( 61 ’name’ => ’title’, 62 ’required’ => true, 63 ’filters’ => array( 64 array(’name’ => ’StripTags’), 65 array(’name’ => ’StringTrim’), 66 ), 67 ’validators’ => array( 68 array( 69 ’name’ => ’StringLength’, 70 ’options’ => array( 71 ’encoding’ => ’UTF-8’, 72 ’min’ => 1, 73 ’max’ => 100, 74 ), 75 ), 76 ), 77 )); 78 79 $this->inputFilter = $inputFilter; 80 } 81 82 return $this->inputFilter; 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 doesn’t enter more characters than we can store into the database. We now need to get the form to display and then process it on submission. This is done within the AlbumController’s addAction(): 9.1. Adding new albums 25
  • 66. Zend Framework 2 Documentation, Release 2.3.1dev 1 // module/Album/src/Album/Controller/AlbumController.php: 2 3 //... 4 use ZendMvcControllerAbstractActionController; 5 use ZendViewModelViewModel; 6 use AlbumModelAlbum; // <-- Add this import 7 use AlbumFormAlbumForm; // <-- Add this import 8 //... 9 10 // Add content to this method: 11 public function addAction() 12 { 13 $form = new AlbumForm(); 14 $form->get(’submit’)->setValue(’Add’); 15 16 $request = $this->getRequest(); 17 if ($request->isPost()) { 18 $album = new Album(); 19 $form->setInputFilter($album->getInputFilter()); 20 $form->setData($request->getPost()); 21 22 if ($form->isValid()) { 23 $album->exchangeArray($form->getData()); 24 $this->getAlbumTable()->saveAlbum($album); 25 26 // Redirect to list of albums 27 return $this->redirect()->toRoute(’album’); 28 } 29 } 30 return array(’form’ => $form); 31 } 32 //... After adding the AlbumForm to the use list, we implement addAction(). Let’s look at the addAction() code in a little more detail: 1 $form = new AlbumForm(); 2 $form->get(’submit’)->setValue(’Add’); We instantiate AlbumForm and set the label on the submit button to “Add”. We do this here as we’ll want to re-use the form when editing an album and will use a different label. 1 $request = $this->getRequest(); 2 if ($request->isPost()) { 3 $album = new Album(); 4 $form->setInputFilter($album->getInputFilter()); 5 $form->setData($request->getPost()); 6 if ($form->isValid()) { If the Request object’s isPost() method is true, then the form has been submitted and so we set the form’s 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 $album->exchangeArray($form->getData()); 2 $this->getAlbumTable()->saveAlbum($album); If the form is valid, then we grab the data from the form and store to the model using saveAlbum(). 26 Chapter 9. Forms and actions
  • 67. Zend Framework 2 Documentation, Release 2.3.1dev 1 // Redirect to list of albums 2 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 <?php 2 // module/Album/view/album/album/add.phtml: 3 4 $title = ’Add new album’; 5 $this->headTitle($title); 6 ?> 7 <h1><?php echo $this->escapeHtml($title); ?></h1> 8 <?php 9 $form->setAttribute(’action’, $this->url(’album’, array(’action’ => ’add’))); 10 $form->prepare(); 11 12 echo $this->form()->openTag($form); 13 echo $this->formHidden($form->get(’id’)); 14 echo $this->formRow($form->get(’title’)); 15 echo $this->formRow($form->get(’artist’)); 16 echo $this->formSubmit($form->get(’submit’)); 17 echo $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(). 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. 9.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: 9.2. Editing an album 27
  • 68. Zend Framework 2 Documentation, Release 2.3.1dev 1 // module/Album/src/Album/Controller/AlbumController.php: 2 //... 3 4 // Add content to this method: 5 public function editAction() 6 { 7 $id = (int) $this->params()->fromRoute(’id’, 0); 8 if (!$id) { 9 return $this->redirect()->toRoute(’album’, array( 10 ’action’ => ’add’ 11 )); 12 } 13 14 // Get the Album with the specified id. An exception is thrown 15 // if it cannot be found, in which case go to the index page. 16 try { 17 $album = $this->getAlbumTable()->getAlbum($id); 18 } 19 catch (Exception $ex) { 20 return $this->redirect()->toRoute(’album’, array( 21 ’action’ => ’index’ 22 )); 23 } 24 25 $form = new AlbumForm(); 26 $form->bind($album); 27 $form->get(’submit’)->setAttribute(’value’, ’Edit’); 28 29 $request = $this->getRequest(); 30 if ($request->isPost()) { 31 $form->setInputFilter($album->getInputFilter()); 32 $form->setData($request->getPost()); 33 34 if ($form->isValid()) { 35 $this->getAlbumTable()->saveAlbum($album); 36 37 // Redirect to list of albums 38 return $this->redirect()->toRoute(’album’); 39 } 40 } 41 42 return array( 43 ’id’ => $id, 44 ’form’ => $form, 45 ); 46 } 47 //... This code should look comfortably familiar. Let’s 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 $id = (int) $this->params()->fromRoute(’id’, 0); 2 if (!$id) { 3 return $this->redirect()->toRoute(’album’, array( 4 ’action’ => ’add’ 5 )); 6 } 7 28 Chapter 9. Forms and actions
  • 69. Zend Framework 2 Documentation, Release 2.3.1dev 8 // Get the album with the specified id. An exception is thrown 9 // if it cannot be found, in which case go to the index page. 10 try { 11 $album = $this->getAlbumTable()->getAlbum($id); 12 } 13 catch (Exception $ex) { 14 return $this->redirect()->toRoute(’album’, array( 15 ’action’ => ’index’ 16 )); 17 } 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 $form = new AlbumForm(); 2 $form->bind($album); 3 $form->get(’submit’)->setAttribute(’value’, ’Edit’); The form’s 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 // module/Album/src/Album/Model/Album.php: 2 // ... 3 public function exchangeArray($data) 4 { 5 $this->id = (isset($data[’id’])) ? $data[’id’] : null; 6 $this->artist = (isset($data[’artist’])) ? $data[’artist’] : null; 7 $this->title = (isset($data[’title’])) ? $data[’title’] : null; 8 } 9 10 // Add the following method: 11 public function getArrayCopy() 12 { 13 return get_object_vars($this); 14 } 15 // ... As a result of using bind() with its hydrator, we do not need to populate the form’s data back into the $album as that’s 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 <?php 2 // module/Album/view/album/album/edit.phtml: 3 4 $title = ’Edit album’; 5 $this->headTitle($title); 6 ?> 9.2. Editing an album 29
  • 70. Zend Framework 2 Documentation, Release 2.3.1dev 7 <h1><?php echo $this->escapeHtml($title); ?></h1> 8 9 <?php 10 $form = $this->form; 11 $form->setAttribute(’action’, $this->url( 12 ’album’, 13 array( 14 ’action’ => ’edit’, 15 ’id’ => $this->id, 16 ) 17 )); 18 $form->prepare(); 19 20 echo $this->form()->openTag($form); 21 echo $this->formHidden($form->get(’id’)); 22 echo $this->formRow($form->get(’title’)); 23 echo $this->formRow($form->get(’artist’)); 24 echo $this->formSubmit($form->get(’submit’)); 25 echo $this->form()->closeTag(); The only changes are to use the ‘Edit Album’ title and set the form’s action to the ‘edit’ action too. You should now be able to edit albums. 9.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 it’s clicked. This would be wrong. Remembering our HTTP spec, we recall that you shouldn’t 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, we’ll code it directly into our view (ZendForm is, after all, optional!). Let’s start with the action code in AlbumController::deleteAction(): 1 // module/Album/src/Album/Controller/AlbumController.php: 2 //... 3 // Add content to the following method: 4 public function deleteAction() 5 { 6 $id = (int) $this->params()->fromRoute(’id’, 0); 7 if (!$id) { 8 return $this->redirect()->toRoute(’album’); 9 } 10 11 $request = $this->getRequest(); 12 if ($request->isPost()) { 13 $del = $request->getPost(’del’, ’No’); 14 15 if ($del == ’Yes’) { 16 $id = (int) $request->getPost(’id’); 17 $this->getAlbumTable()->deleteAlbum($id); 18 } 19 20 // Redirect to list of albums 21 return $this->redirect()->toRoute(’album’); 22 } 30 Chapter 9. Forms and actions
  • 71. Zend Framework 2 Documentation, Release 2.3.1dev 23 24 return array( 25 ’id’ => $id, 26 ’album’ => $this->getAlbumTable()->getAlbum($id) 27 ); 28 } 29 //... As before, we get the id from the matched route, and check the request object’s 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 <?php 2 // module/Album/view/album/album/delete.phtml: 3 4 $title = ’Delete album’; 5 $this->headTitle($title); 6 ?> 7 <h1><?php echo $this->escapeHtml($title); ?></h1> 8 9 <p>Are you sure that you want to delete 10 ’<?php echo $this->escapeHtml($album->title); ?>’ by 11 ’<?php echo $this->escapeHtml($album->artist); ?>’? 12 </p> 13 <?php 14 $url = $this->url(’album’, array( 15 ’action’ => ’delete’, 16 ’id’ => $this->id, 17 )); 18 ?> 19 <form action="<?php echo $url; ?>" method="post"> 20 <div> 21 <input type="hidden" name="id" value="<?php echo (int) $album->id; ?>" /> 22 <input type="submit" name="del" value="Yes" /> 23 <input type="submit" name="del" value="No" /> 24 </div> 25 </form> 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. 9.4 Ensuring that the home page displays the list of albums One final point. At the moment, the home page, http://zf2-tutorial.localhost/ doesn’t display the list of albums. This is due to a route set up in the Application module’s module.config.php. To change it, open module/Application/config/module.config.php and find the home route: 1 ’home’ => array( 2 ’type’ => ’ZendMvcRouterHttpLiteral’, 3 ’options’ => array( 4 ’route’ => ’/’, 5 ’defaults’ => array( 9.4. Ensuring that the home page displays the list of albums 31
  • 72. Zend Framework 2 Documentation, Release 2.3.1dev 6 ’controller’ => ’ApplicationControllerIndex’, 7 ’action’ => ’index’, 8 ), 9 ), 10 ), Change the controller from ApplicationControllerIndex to AlbumControllerAlbum: 1 ’home’ => array( 2 ’type’ => ’ZendMvcRouterHttpLiteral’, 3 ’options’ => array( 4 ’route’ => ’/’, 5 ’defaults’ => array( 6 ’controller’ => ’AlbumControllerAlbum’, // <-- change here 7 ’action’ => ’index’, 8 ), 9 ), 10 ), That’s it - you now have a fully working application! 32 Chapter 9. Forms and actions
  • 73. CHAPTER 10 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. 33
  • 74. Zend Framework 2 Documentation, Release 2.3.1dev 34 Chapter 10. Conclusion
  • 75. CHAPTER 11 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. We will develop this application using Zend Studio 10 and run the application on Zend Server 6. Zend Server is a PHP application server that includes the PHP runtime. It comes in both free and paid editions, both of which provide lots of features; however the most interesting ones for developers are the dead-simple environment setup and the ability to investigate application problems, including profiling performance and memory issues with code-tracing abilities. Zend Server also ships with Zend Framework 2, which is convenient. Zend Studio is an a PHP-focused IDE based on Eclipse that comes in two flavours: the free Eclipse PDT and Zend Studio, a paid-for product that provides enhanced features and support options. Usefully, Eclipse PDT provides Zend Framework 2 support out of the box along with Zend Server integration. You don’t get the mobile features though, or integrated PHP Documenter & PHPUnit features. In this tutorial we’re going to build a small, simple database application to manage a list of to-do items. We’ll need a list of items along with the ability to add, edit and delete items. We’ll use a database to store information about each to-do item. 11.1 Installation Firstly you’ll need to install Zend Server and Eclipse PDT. If you have a license for Zend Studio 10, you can use that too. You can download the latest version of Zend Server. Grab Eclipse PDT or Zend Studio (which comes with a free 30-day trial) and install it. In this tutorial we will use the phrase Zend Studio, but it will all work with Eclipse PDT too. On Linux, you can install Zend Server with either Apache or Nginx. This tutorial has assumed that you have installed the Apache version. The only important difference for this tutorial is the creation of rewrite rules. Once you have installed Zend Server, enter the administration application, which can usually be found at http://localhost:10081/. Set the time zone in Configuration -> PHP, and then restart the server (third button from the right in the top right corner). You will also need to install MySQL using your Linux distribution’s package manager or from mysql.com if you are on Windows. For OS X users, Zend Server already includes MySQL for you. On OS X, the document root for the Zend Server installed Apache is at /usr/local/zend/apache2/htdocs. On Linux, Zend Server uses the web server supplied by the distribution. On Ubuntu 12.04, with Apache, it is /var/www and with nginx it is at /usr/share/nginx/html. On Windows, it is C:Program Files (x86)ZendApache2htdocs. 35
  • 76. Zend Framework 2 Documentation, Release 2.3.1dev Ensure that this folder is writeable by your own user. The easiest way to do this is to change the owner of the html directory. On a Mac, this would be: $ sudo chown {your username} /usr/local/zend/apache2/htdocs 11.2 Getting Started We start by creating a new Local PHP project in Zend Studio. Open Zend Studio and select File -> New -> Local PHP Project. This will display the New Local PHP Project wizard as shown: Enter MyTaskList as the Project Name and set the location to the Zend Server document root. Due to the integration between Zend Server and Zend Studio, you should find the correct directory as an option in the drop down list. Select Zend Framework as the Content and you can then select which version of Zend Framework to use. Select the latest Zend Framework 2 version and press Next. The next step is the Launch Settings tab. Choose Launch URL and set the host to http://localhost (or http://localhost:10088 on OS X) and the Base Path to /MyTaskList/: Press Finish to create your new project in Zend Studio. Zend Studio has now created a default Zend Framework project for us: This is a standard Zend Framework 2 Skeleton Application and is a great starting point for a new ZF2 application. To set up Zend Studio to run this project, select Run -> Run Configurations... and double click on PHP Web Appli- cation in the left hand list. Enter MyTaskList as the name, Local Zend Server as the PHP Server and then click the Browse button and select index.php within the public folder of the MyTaskList project. Uncheck Auto Generate in the URL section and then set the path to /MyTaskList/public and press Apply and then Close: To test that all is working, press the run button in the toolbar (white arrow in a green circle). The ZF2 Skeleton Application home page will display in a new tab within Zend Studio: You can also navigate to the same URL (http://localhost:10088/MyTaskList/public/ on a Mac) in any browser. We have successfully installed both Zend Server and Zend Studio, created a project and tested it. Let’s start by looking at what we have so far in our Zend Framework project. 36 Chapter 11. Getting Started with Zend Framework 2
  • 77. CHAPTER 12 A quick tour of the skeleton application The skeleton application provides a lot of files, so it’s worth having a quick high-level look at what has been generated for us. There are a number of high level directories created for us (along with Composer and other support files): Folder Information stored config Application-level configuration files. data Data files generated by the application, such as caches. module The source files that make up this application are stored within separate modules within this folder. public The web server’s document root. All files served directly by the web server are in here. vendor Third party libraries. One of the key features of Zend Framework 2 is its module system. This provides organisation within your application; all application code lives within a module. The skeleton provides the Application module for bootstrapping, error and routing configuration. It also provides the application-level controllers for the home page and error display. The Application module contains these key folders: Folder Information stored config Module-specific configuration files. language Translation files. src/ApplicationPHP files for this module, including controller and model files. The controller for the home page, IndexController.php, is provided. view/applicationView scripts for each controller action. view/error Error view scripts for 404 and generic errors. view/layout Layout view scripts. These contain the common HTML shared by a number of pages within the website. An initial default file, layout.phtml, is provided. Modules are simply namespaces containing a top level Module class. They are intended to be reusable and no additional constraints are placed on how they are organised. An application consists of multiple modules, both third party and application specific, with the list of modules to load stored in config/application.config.php. 12.1 The dispatch cycle Zend Framework 2 applications use the Front Controller design pattern. This means that all requests are directed to a single entry point, the public/index.php file. This is done using a .htaccess file containing rewrite rules that serves all static files (such as CSS & Javascript) and directs all other requests to the index.php. The index.php file initialises the autoloader and then bootstraps ZendMvcApplication before finally running the application. The process looks like this: 37
  • 78. Zend Framework 2 Documentation, Release 2.3.1dev 12.1.1 Starting up To set up the application for running, a number of things happen. Firstly an instance of ZendServiceManger is created as the master locator for all class instances used by the application. The Module Manager is then used to load all the application’s modules. It does this by reading its configuration file, application.config.php, which is solely for use by the Module Manager and does not contain the configuration used by the application itself. The modules are loaded in the order listed in the configuration file and for each module a number of steps takes place: • Configuration of autoloading. • Loading of module configuration. • Registration of event listeners. • Configuration of the Service Manager. The configuration information from all modules is merged together into one configuration array. This means that con- figuration information in subsequent modules can override information already set. Finally, the global configuration files stored in the config/autoload directory are merged (the *.global.php and then the *.local.php files). This means that any module’s configuration can be overridden at the application level and is a key feature that helps to ensure that the code within a third-party module does not need to be changed. The Service Manager and Event Manager are two other key features of a Zend Framework 2 application. ZendServiceManager allows for decoupling the instantiation and configuration of a class and its dependencies from where that class is used. This is known as Dependency Injection and is used extensively in Zend Framework 2. ZendEventManager is an implementation of the Observer design pattern which allows decoupling of code. In Zend Framework 2, every key process in the dispatch cycle is implemented as an event. This means that you can write lis- teners for these events which can then change the flow of operation or perform additional processes when something else has happened. 12.1.2 Dispatching Once all modules have been loaded, the application is run. This is done as a series of events, with the first event, route, used to determine the controller action that should be run based on the URL requested. Once this is determined, the dispatch event is triggered which causes the action method within the controller class to be executed. The view rendering event, render, is then triggered if an HTML view is required. Finally the finish event is triggered which sends the response back to the user’s web browser. While this is a typical dispatch cycle, Zend Framework 2’s dispatch system is very flexible and can be configured in a variety of ways depending on the specific application. Now that we’ve looked at how Zend Framework works, let’s move on and write the MyTaskList application. 38 Chapter 12. A quick tour of the skeleton application
  • 79. CHAPTER 13 The MyTaskList application The application we are going to create is a to-do list manager. The application will allow us to create to-do items and check them off. We’ll also need the ability to edit and delete an item. As we are building a simple application, we need just four pages: Page Notes Checklist homepage This will display the list of to-do items. Add new item This page will provide a form for adding a new item. Edit item This page will provide a form for editing an item. Delete item This page will confirm that we want to delete an item and then delete it. Each page of the application is known as an action, and actions are grouped into controllers within modules. Generally, related actions are placed into a single controller; for instance, a news controller might have actions of current, archived and view. We will store information about our to-do items in a database. A single table will suffice with the following fields: Field name Type Null? Notes id integer No Primary key, auto-increment title varchar(100) No Name of the file on disk completed tinyint No Zero if not done, one if done created datetime No Date that the to-do item was created We are going to use MySQL, via PHP’s PDO driver, so create a database called mytasklist using your preferred MySQL client, and run these SQL statements to create the task_item table and some sample data: CREATE TABLE task_item ( id INT NOT NULL AUTO_INCREMENT, title VARCHAR(100) NOT NULL, completed TINYINT NOT NULL DEFAULT ’0’, created DATETIME NOT NULL, PRIMARY KEY (id) ); INSERT INTO task_item (title, completed, created) VALUES (’Purchase conference ticket’, 0, NOW()); INSERT INTO task_item (title, completed, created) VALUES (’Book airline ticket’, 0, NOW()); INSERT INTO task_item (title, completed, created) VALUES (’Book hotel’, 0, NOW()); INSERT INTO task_item (title, completed, created) VALUES (’Enjoy conference’, 0, NOW()); 39
  • 80. Zend Framework 2 Documentation, Release 2.3.1dev Note that if you have Zend Studio, you can use the built-in Database Connectivity features. This if found in the Database Development perspective (Window | Open Perspective | Other | Database Development menu item) and further details are in the Zend Studio manual. 13.1 The Checklist module We will create all our code within a module called Checklist. The Checklist module will, therefore, contain our controllers, models, forms and views, along with specific configuration files. We create our new Checklist module in Zend Studio. In the PHP Explorer on the left, right click on the MyTaskList project folder and choose New -> Zend Framework Item. Click on Zend Module and press Next. The Source Folder should already be set to /MyTaskList/module. Enter Checklist as the Module name and Task as the Controller name and then press Finish: The wizard will now go ahead and create a blank module for us and register it with the Module Manager’s application.config.php. You can see what it has done in the PHP Explorer view under the module folder: As you can see the Checklist module has separate directories for the different types of files we will have. The config folder contains configuration files, and the PHP files that contain classes within the Checklist namespace live in the src/Checklist directory. The view directory also has a sub- folder called checklist for our module’s view scripts, and the tests folder contains PHPUnit test files. 13.2 The Module class As mentioned earlier, a module’s Module class contains methods that are called during the start-up process and is also used to register listeners that will be triggered during the dispatch process. The Module class created for us contains three methods: getAutoloaderConfig(), getConfig() and onBootstrap() which are called by the Module Manager during start-up. 13.2.1 Autoloading files Our getAutoloaderConfig() method returns an array that is compatible with ZF2’s AutoloaderFactory. It is configured for us with both a classmap file (autoload_classmap.php) and a standard autoloader to load any files in src/Checklist according to the PSR-0 rules . Classmap autoloading is faster, but requires adding each new class you create to the array within the au- toload_classmap.php file, which slows down development. The standard autoloader, however, doesn’t have this re- quirement and will always load a class if its file is named correctly. This allows us to develop quickly by creating new classes when we need them and then gain a performance boost by using the classmap autoloader in production. Zend Framework 2 provides bin/classmap_generator.php to create and update the file. 13.2.2 Configuration The getConfig() method in ChecklistModule is called by the Module Manager to retrieve the configuration information for this module. By tradition, this method simply loads the config/module.config.php file which is an associative array. In practice, the Module Manager requires that the returned value from getConfig() be a Traversable, which means that you can use any configuration format that ZendConfig supports. You will find, though, that most examples use arrays as they are easy to understand and fast. The actual configuration information is placed in config/module.config.php. This nested array provides the key configuration for our module. The controllers sub-array is used to register this module’s controller classes 40 Chapter 13. The MyTaskList application
  • 81. Zend Framework 2 Documentation, Release 2.3.1dev with the Controller Service Manager which is used by the dispatcher to instantiate a controller. The one controller that we need, TaskController, is already registered for us. The router sub-array provides the configuration of the routes that are used by this module. A route is the way that a URL is mapped to a to a particular action method within a controller class. Zend Studio’s default configuration is set up so that a URL of /checklist/foo/bar maps to the barAction() method of the FooController within the Checklist module. We will modify this later. Finally, the view_manager sub-array within the module.config.php file is used to register the directory where our view files are with the View sub- system. This means that within the view/checklist sub-folder, there is a folder for each controller. We have one controller, TaskController, so there is a single sub-folder in view/checklist called task. Within this folder, there are separate .phtml files which contain the specific HTML for each action of our module. 13.2.3 Registering events The onBootstrap() method in the Module class is the easiest place to register listeners for the MVC events that are triggered by the Event Manager. Note that the default method body provided by Zend Studio is not needed as the ModuleRouteListener is already registered by the Application module. We do not have to register any events for this tutorial, so go ahead and delete the entire OnBootstrap() method. 13.2. The Module class 41
  • 82. Zend Framework 2 Documentation, Release 2.3.1dev 42 Chapter 13. The MyTaskList application
  • 83. CHAPTER 14 The application’s pages As we have four pages that all apply to tasks, we will group them in a single controller called TaskController within our Checklist module as four actions. Each action has a related URL which will result in that action being dispatched. The four actions and URLs are: Page URL Action Homepage /task index Add new task /task/add add Edit task /task/edit edit Delete task /task/delete delete The mapping of a URL to a particular action is done using routes that are defined in the module’s module.config.php file. As noted earlier, the configuration file, module.config.php created by Zend Stu- dio has a route called checklist set up for us. 14.1 Routing The default route provided for us isn’t quite what we need. The checklist route is defined like this: module/Checklist/src/config/module.config.php: ’router’ => array( ’routes’ => array( ’checklist’ => array( ’type’ => ’Literal’, ’options’ => array( ’route’ => ’/task’, ’defaults’ => array( ’__NAMESPACE__’ => ’ChecklistController’, ’controller’ => ’Task’, ’action’ => ’index’, ), ), ’may_terminate’ => true, ’child_routes’ => array( ’default’ => array( ’type’ => ’Segment’, ’options’ => array( ’route’ => ’/[:controller[/:action]]’, ), ), 43
  • 84. Zend Framework 2 Documentation, Release 2.3.1dev ), ), This defines a main route called checklist, which maps the URL /task to the index action of the Task controller and then there is a child route called default which maps /task/{controller name}/{action name} to the {action name} action of the {controller name} controller. This means that, by default, the URL to call the add action of the Task controller would be /task/task/add. This doesn’t look very nice and we would like to shorten it to /task/add. To fix this, we will rename the route from checklist to task because this route will be solely for the Task controller. We will then redefine it to be a single Segment type route that can handle actions as well as just route to the index action Open module/Checklist/config/module.config.php in Zend Studio and change the entire router sec- tion of the array to be: module/Checklist/src/config/module.config.php: ’router’ => array( ’routes’ => array( ’task’ => array( ’type’ => ’Segment’, ’options’ => array( ’route’ => ’/task[/:action[/:id]]’, ’defaults’ => array( ’__NAMESPACE__’ => ’ChecklistController’, ’controller’ => ’Task’, ’action’ => ’index’, ), ’constraints’ => array( ’action’ => ’^add|edit|delete$’, ’id’ => ’[0-9]+’, ), ), ), ), ), We have now renamed the route to task and have set it up as a Segment route with two optional parameters in the URL: action and id. We have set a default of index for the action, so that if the URL is simply /task, then we shall use the index action in our controller. The optional constraints section allow us to specify regular expression patterns that match the characters that we expect for a given parameter. For this route, we have specified that the action parameter must be either add, edit or delete and that the id parameter must only contain numbers. The routing for our Checklist module is now set up, so we can now turn our attention to the controller. 14.2 The TaskController In Zend Framework 2, the controller is a class that is generally called {Controller name}Controller. Note that {Controller name} starts 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’s the module/Checklist/src/Checklist/Controller directory. Each action is a public function within the controller class that is named {action name}Action. In this case {action name} should start with a lower case letter. 44 Chapter 14. The application’s pages
  • 85. Zend Framework 2 Documentation, Release 2.3.1dev Note that this is merely a convention. Zend Framework 2’s only restrictions on a controller is that it must implement the ZendStdlibDispatchable interface. The framework provides two abstract classes that do this for us: ZendMvcControllerActionController and ZendMvcControllerRestfulController. We’ll be using the ActionController, but if you’re intending to write a RESTful web service, RestfulController may be useful. Zend Studio’s module creation wizard has already created TaskController for us with two action methods in it: indexAction() and fooAction(). Remove the fooAction() method and the default “Copyright Zend” DocBlock comment at the top of the file. Your controller should now look like this: module/Checklist/src/Checklist/Controller/TaskController.php: namespace ChecklistController; use ZendMvcControllerAbstractActionController; class TaskController extends AbstractActionController { public function indexAction() { return array(); } } This controller now contains the action for the home page which will display our list of to-do items. We now need to create a model-layer that can retrieve the tasks from the database for display. 14.3 The model It is time to look at the model section of our application. Remember that the model is the part that deals with the application’s core purpose (the so-called “business rules”) and, in our case, deals with the database. Zend Framework does not provide a ZendModel component because the model is your business logic and it’s 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 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 fairly simple model layer using an entity and a mapper that uses the ZendDb component. In a larger, more complex, application, you would probably also have a service class that interfaces between the controller and the mapper. We already have created the database table and added some sample data, so let’s start by creating an entity object. An entity object is a simple PHP object that represents a thing in the application. In our case, it represents a task to be completed, so we will call it TaskEntity. Create a new folder in module/Checklist/src/Checklist called Model and then right click on the new Model folder and choose New -> PHP File. In the New PHP File dialog, set the File Name to TaskEntity.php as shown and then press Finish. This will create a blank PHP file. Update it so that it looks like this: module/Checklist/src/Checklist/TaskEntity.php: <?php namespace ChecklistModel; class TaskEntity 14.3. The model 45
  • 86. Zend Framework 2 Documentation, Release 2.3.1dev { protected $id; protected $title; protected $completed = 0; protected $created; public function __construct() { $this->created = date(’Y-m-d H:i:s’); } public function getId() { return $this->id; } public function setId($Value) { $this->id = $Value; } public function getTitle() { return $this->title; } public function setTitle($Value) { $this->title = $Value; } public function getCompleted() { return $this->completed; } public function setCompleted($Value) { $this->completed = $Value; } public function getCreated() { return $this->created; } public function setCreated($Value) { $this->created = $Value; } } The Task entity is a simple PHP class with four properties with getter and setter methods for each property. We also have a constructor to fill in the created property. If you are using Zend Studio rather than Eclipse PDT, then you can generate the getter and setter methods by right clicking in the file and choosing Source -> Generate Getters and Setters. We now need a mapper class which is responsible for persisting task entities to the database and populating them 46 Chapter 14. The application’s pages
  • 87. Zend Framework 2 Documentation, Release 2.3.1dev with new data. Again, right click on the Model folder and choose New -> PHP File and create a PHP file called TaskMapper.php. Update it so that it looks like this: module/Checklist/src/Checklist/TaskMapper.php: <?php namespace ChecklistModel; use ZendDbAdapterAdapter; use ChecklistModelTaskEntity; use ZendStdlibHydratorClassMethods; use ZendDbSqlSql; use ZendDbSqlSelect; use ZendDbResultSetHydratingResultSet; class TaskMapper { protected $tableName = ’task_item’; protected $dbAdapter; protected $sql; public function __construct(Adapter $dbAdapter) { $this->dbAdapter = $dbAdapter; $this->sql = new Sql($dbAdapter); $this->sql->setTable($this->tableName); } public function fetchAll() { $select = $this->sql->select(); $select->order(array(’completed ASC’, ’created ASC’)); $statement = $this->sql->prepareStatementForSqlObject($select); $results = $statement->execute(); $entityPrototype = new TaskEntity(); $hydrator = new ClassMethods(); $resultset = new HydratingResultSet($hydrator, $entityPrototype); $resultset->initialize($results); return $resultset; } } Within this mapper class we have implemented the fetchAll() method and a constructor. There’s quite a lot going on here as we’re dealing with the ZendDb component, so let’s break it down. Firstly we have the construc- tor which takes a ZendDbAdapterAdapter parameter as we can’t do anything without a database adapter. ZendDbSql is an object that abstracts SQL statements that are compatible with the underlying database adapter in use. We are going to use this object for all of our interaction with the database, so we create it in the constructor. The fetchAll() method retrieves data from the database and places it into a HydratingResultSet which is able to return populated TaskEntity objects when iterating. To do this, we have three distinct things happening. Firstly we retrieve a Select object from the Sql object and use the order() method to place completed items last. We then create a Statement object and execute it to retrieve the data from the database. The $results object can be iterated over, but will return an array for each row retrieved but we want a ‘‘ TaskEntity‘‘ object. To get this, we create a HydratingResultSet which requires a hydrator and an entity prototype to work. The hydrator is an object that knows how to populate an entity. As there are many ways to create an entity object, there are multiple hydrator objects provided with ZF2 and you can create your own. For our TaskEntity, we use 14.3. The model 47
  • 88. Zend Framework 2 Documentation, Release 2.3.1dev the ClassMethods hydrator which expects a getter and a setter method for each column in the resultset. Another useful hydrator is ArraySerializable which will call getArrayCopy() and populate() on the entity object when transferring data. The HydratingResultSet uses the prototype design pattern when creating the entities when iterating. This means that instead of instantiating a new instance of the entity class on each iteration, it clones the provided instantiated object. See http://ralphschindler.com/2012/03/09/php- constructor-best-practices- and-the-prototype-pattern for more details. Finally, fetchAll() returns the result set object with the correct data in it. 14.4 Using Service Manager to configure the database credentials and inject into the controller In order to always use the same instance of our TaskMapper, we will use the Service Manager to define how to create the mapper and also to retrieve it when we need it. This is most easily done in the Module class where we create a method called getServiceConfig() which is automatically called by the Module Manager and applied to the Service Manager. We’ll then be able to retrieve it in our controller when we need it. To configure the Service Manager we can either supply the name of the class to be instantiated or create a factory (closure or callback) method that instantiates the object when the Service Manager needs it. We start by implementing getServiceConfig() and write a closure that creates a TaskMapper instance. Add this method to the Module class: ** module/Checklist/Module.php:** class Module { public function getServiceConfig() { return array( ’factories’ => array( ’TaskMapper’ => function ($sm) { $dbAdapter = $sm->get(’ZendDbAdapterAdapter’); $mapper = new TaskMapper($dbAdapter); return $mapper; } ), ); } // ... Don’t forget to add use ChecklistModelTaskMapper; to the list of use statements at the top of the file. The getServiceConfig() method returns an array of class creation definitions that are all merged together by the Module Manager before passing to the Service Manager. To create a service within the Service Manager we use a unique key name, TaskMapper. As this has to be unique, it’s common (but not a requirement) to use the fully qualified class name as the Service Manager key name. We then define a closure that the Service Manager will call when it is asked for an instance of TaskMapper. We can do anything we like in this closure, as long as we return an instance of the required class. In this case, we retrieve an instance of the database adapter from the Service Manager and then instantiate a TaskMapper object and return it. This is an example of the Dependency Injection pattern at work as we have injected the database adapter into the mapper. This also means that Service Manager can be used as a Dependency Injection Container in addition to a Service Locator. As we have requested an instance of ZendDbAdapterAdapter from the Service Manager, we also need to configure the Service Manager so that it knows how to instantiate a ZendDbAdapterAdapter. This is done using a class provided by Zend Framework called ZendDbAdapterAdapterServiceFactory which we can configure within the merged configuration system. As we noted earlier, the Module Manager merges all the configuration from each module and then merges in the files in the config/autoload directory (*.global.php 48 Chapter 14. The application’s pages
  • 89. Zend Framework 2 Documentation, Release 2.3.1dev and then *.local.php files). We’ll add our database configuration information to global.php which you should commit to your version control system.You can then use local.php (outside of the VCS) to store the credentials for your database. Open config/autoload/global.php and replace the empty array with: config/autoload/global.php: return array( ’service_manager’ => array( ’factories’ => array( ’ZendDbAdapterAdapter’ => ’ZendDbAdapterAdapterServiceFactory’, ), ), ’db’ => array( ’driver’ => ’Pdo’, ’dsn’ => ’mysql:dbname=mytasklist;hostname=localhost’, ’driver_options’ => array( PDO::MYSQL_ATTR_INIT_COMMAND => ’SET NAMES ’UTF8’’ ), ), ); Firstly, we provide additional Service Manager configuration in the service_manager section, This array works exactly the same as the one in getServiceConfig(), except that you should not use closures in a con- fig file as if you do Module Manager will not be able to cache the merged configuration information. As we already have an implementation for creating a ZendDbAdapterAdapter, we use the factories sub-array to map the key name of ZendDbAdapterAdapter to the string name of the factory class (ZendDbAdapterAdapterServiceFactory‘) and the Service Manager will then use ZendDbAdapter- AdapterServiceFactory to instantiate a database adapter for us. The ZendDbAdapterAdapterServiceFactory object looks for a key called db in the configuration array and uses this to configure the database adapter. Therefore, we create the db key in our global.php file with the relevant configuration data. The only data that is missing is the username and password required to connect to the database. We do not want to store this in the version control system, so we store this in the local.php configuration file, which, by default, is ignored by git. Open config/autoload/local.php and replace the empty array with: config/autoload/global.php: return array( ’db’ => array( ’username’ => ’YOUR_USERNAME’, ’password’ => ’YOUR_PASSWORD’, ), ); Obviously you should replace YOUR_USERNAME and YOUR_PASSWORD with the correct credentials. Now that the Service Manager can create a TaskMapper instance for us, we can add a method to the controller to retrieve it. Add getTaskMapper() to the TaskController class: module/Checklist/src/Checklist/Controller/TaskController.php: public function getTaskMapper() { $sm = $this->getServiceLocator(); return $sm->get(’ChecklistModelTaskMapper’); } 14.4. Using Service Manager to configure the database credentials and inject into the controller 49
  • 90. Zend Framework 2 Documentation, Release 2.3.1dev We can now call getTaskMapper() from within our controller whenever we need to interact with our model layer. Let’s start with a list of tasks when the index action is called. 50 Chapter 14. The application’s pages
  • 91. CHAPTER 15 Listing tasks In order to list the tasks, we need to retrieve them from the model layer and pass them to the view. To do this, we fill in indexAction() within TaskController. Update the indexAction() like this: module/Checklist/src/Checklist/Controller/TaskController.php: public function indexAction() { $mapper = $this->getTaskMapper(); return new ViewModel(array(’tasks’ => $mapper->fetchAll())); } You’ll also need to add use ZendViewModelViewModel; to list of use statements at the top of the file. To provide variables to the view layer, 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}. You can also return an array from a controller as Zend Framework will construct a ViewModel behind the scenes for you. We can now fill in the task/index.phtml view script. Replace the contents with this new code: module/Checklist/view/checklist/task/index.phtml: <?php $title = ’My task list’; $this->headTitle($title); ?> <h1><?php echo $this->escapeHtml($title); ?></h1> <p><a href="<?php echo $this->url(’task’, array( ’action’=>’add’));?>">Add new item</a></p> <table class="table"> <tr> <th>Task</th> <th>Created</th> <th>Completed?</th> <th>&nbsp;</th> </tr> <?php foreach ($tasks as $task): ?> <tr> <td> <a href="<?php echo $this->url(’task’, array(’action’=>’edit’, ’id’ => $task->getId()));?>"> 51
  • 92. Zend Framework 2 Documentation, Release 2.3.1dev <?php echo $this->escapeHtml($task->getTitle()); ?></a> </td> <td><?php echo $this->escapeHtml($task->getCreated()); ?></td> <td><?php echo $task->getCompleted() ? ’Yes’ : ’No’; ?></td> <td> <a href="<?php echo $this->url(’task’, array(’action’=>’delete’, ’id’ => $task->getId()));?>">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 browser’s title bar. We then create a link to add a new item using the url() view helper. The url() view helper is provided by Zend Framework and is used to create the links we need. The first parameter to url() is the route name that we wish to use for construction of the URL and then the second parameter is an array of all the variables to fit into the place-holders to use. In this case we use our task route which is set up to accept two place-holder variables: action and id. We iterate over the $tasks that we assigned from the controller action within an HTML table. The Zend Framework view system automatically ensures that these variables are extracted into the scope of the view script. Alternatively, you can also prefix with $this-> if you would like. For each row, we display each task’s title, creation date, completion date and provide links to allow for editing and deleting the record. A standard foreach: loop is used to iterate over the list of tasks, 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 that we always use the escapeHtml() view helper to help protect ourselves from XSS vulnerabilities. If you now run the application from within Zend Studio and navigate to http://localhost:10088/MyTaskList/public/task you should see this: 15.1 Redirect the home page When you first pressed the Run button, you saw the application’s home page which is the skeleton’s welcome page. It would be helpful if we could redirect immediately to /tasks to save us having to edit the URL each time. To do this, go to Navigate -> Open Type... in Zend Studio and type IndexController in the search box of the Open PHP Type dialog and press return. This will open module/Application/src/Application/Controller/IndexController.php for you. Change the indexAction() method so that it reads: module/Application/src/Application/Controller/IndexController.php: public function indexAction() { return $this->redirect()->toRoute(’task’); } We use the redirect controller plugin to redirect the request for the home page to the URL defined by the route name task which we set up earlier. Now, when you press the green “Run” button, you will be taken directly to the list of tasks. 52 Chapter 15. Listing tasks
  • 93. CHAPTER 16 Styling We’ve picked up the skeleton application’s layout which is fine for this tutorial, but we need to change the title and remove the copyright message. The Zend Skeleton Application is set up to use ZendI18n‘s translation functionality for all the text. This allows you to translate all the text strings in the application into a different language if you need to. The translation data is stored in separate files in the gettext format which have the extension .po and are stored in the application/language folder. The title of the application is “Skeleton Application” and to change this, you need to use the poedit application (http://www.poedit.net/download.php/). Start poedit and open application/language/en_US.po. Click on “Skeleton Application” in the list of original strings and then type in “My Task List” as the translation. Press Save in the toolbar and poedit will create an updated en_US.mo file. Alternatively, the gted Eclipse plugin allows for editing PO files directly in Zend Studio or PDT. To install gted, select the Help > Install New Software menu, and press the “Add...” button. Enter the gted for the Name, http://gted.sourceforge.net/update as the Location and then press the “OK” button. You will see the gted name ap- pear in the list. Click on the checkbox next to gted and work through the install wizard by pressing “Next button as required. At the end of the installation you will be able to create or edit the PO files using the gted plugin: It follows that as Zend Studio and PDT are based on Eclipse you can install any other Eclipse plugins that are listed on http://marketplace.eclipse.org/ using the same process. The next thing to do is to remove the copyright message, we need to edit the Application module’s layout.phtml view script: module/Application/view/layout/layout.phtml: Remove this line: <p>&copy; 2005 - <?php echo date(’Y’) ?> by Zend Technologies Ltd. <?php echo $this->translate(’All r The page looks a little better now! 53
  • 94. Zend Framework 2 Documentation, Release 2.3.1dev 54 Chapter 16. Styling
  • 95. CHAPTER 17 Adding new tasks We can now write the functionality to add new tasks. There are two things we need to do: • Display a form for user to provide the task information • Process the form submission and store to database We use ZendForm to do this. The ZendForm component manages the form and works in tandem with the ZendInputFilter component which will provide validation. Create a new folder in module/Checklist/src/Checklist called Form and then within the Form folder, create a new PHP file called TaskForm.php with these contents: module/Checklist/src/Checklist/Form/TaskForm.php: <?php namespace ChecklistForm; use ZendFormForm; use ZendStdlibHydratorClassMethods; class TaskForm extends Form { public function __construct($name = null, $options = array()) { parent::__construct(’task’); $this->setAttribute(’method’, ’post’); $this->setInputFilter(new TaskFilter()); $this->setHydrator(new ClassMethods()); $this->add(array( ’name’ => ’id’, ’type’ => ’hidden’, )); $this->add(array( ’name’ => ’title’, ’type’ => ’text’, ’options’ => array( ’label’ => ’Title’, ), ’attributes’ => array( ’id’ => ’title’, ’maxlength’ => 100, ) 55
  • 96. Zend Framework 2 Documentation, Release 2.3.1dev )); $this->add(array( ’name’ => ’completed’, ’type’ => ’checkbox’, ’options’ => array( ’label’ => ’Completed?’, ’label_attributes’ => array(’class’=>’checkbox’), ), )); $this->add(array( ’name’ => ’submit’, ’attributes’ => array( ’type’ => ’submit’, ’value’ => ’Go’, ’class’ => ’btn btn-primary’, ), )); } } Within the constructor of TaskForm, we set the name when we call the parent’s constructor and then set the method and the input filter that we want to use. We also set the form’s hydrator to be ClassMethods, as a form object uses hydration to transfer data to and from an entity object in exactly the same way as the ZendDb components do. Finally, we create the form elements for the id, title, whether the task is complete and the 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 is this done using an input filter which can either be standalone or within any class that implements InputFilterAwareInterface, such as a model entity. For this application we are going to create a separate class for our input filter. Create a new PHP file called TaskFilter.php in the module/Checklist/src/Checklist/Form folder with these contents: module/Checklist/src/Checklist/Form/TaskFilter.php: <?php namespace ChecklistForm; use ZendInputFilterInputFilter; class TaskFilter extends InputFilter { public function __construct() { $this->add(array( ’name’ => ’id’, ’required’ => true, ’filters’ => array( array(’name’ => ’Int’), ), )); $this->add(array( ’name’ => ’title’, ’required’ => true, ’filters’ => array( array(’name’ => ’StripTags’), 56 Chapter 17. Adding new tasks
  • 97. Zend Framework 2 Documentation, Release 2.3.1dev array(’name’ => ’StringTrim’), ), ’validators’ => array( array( ’name’ => ’StringLength’, ’options’ => array( ’encoding’ => ’UTF-8’, ’max’ => 100 ), ), ), )); $this->add(array( ’name’ => ’completed’, ’required’ => false, )); } } In the constructor for the TaskFilter, we create inputs for each property that we want to filter. Each input can have a name, a required property a list of filters and a list of validators. All are optional other than the name property. The difference between filters and validators is that a filter changes the data passed through it and a validator tests if the data matches some specific criteria. For the title, we filter the string with StripTags and StringTrim and finally ensure that the string is no longer than 100 characters with the StringLength validator. For the completed element, we simply set required to false. We now need to display the form and process it on submission. This is done within the TaskController‘s addAction(). Open TaskController.php (Navigate -> Open Resource... is a convenient way to do this) and add a new method called addAction() to the class that looks like this: module/Checklist/src/Checklist/Controller/TaskController.php: public function addAction() { $form = new TaskForm(); $task = new TaskEntity(); $form->bind($task); $request = $this->getRequest(); if ($request->isPost()) { $form->setData($request->getPost()); if ($form->isValid()) { $this->getTaskMapper()->saveTask($task); // Redirect to list of tasks return $this->redirect()->toRoute(’task’); } } return array(’form’ => $form); } Add use ChecklistModelTaskEntity; and use ChecklistFormTaskForm; to the list of use statements at the top of the file. Let’s look at what the addAction() does in detail. 57
  • 98. Zend Framework 2 Documentation, Release 2.3.1dev $form = new TaskForm(); $task = new TaskEntity(); $form->bind($task); We instantiate a new TaskForm object and an empty TaskEntity which we bind to the form for use by the form later. The form’s bind() method attaches the model to the form. This is used in two ways: 1. When displaying the form, the initial values for each element are extracted from the model. 2. After successful validation in isValid(), the data from the form is put back into the model. When adding a new task, we only need to worry about point 2, however for editing an item, we need data transfer in both directions. $request = $this->getRequest(); if ($request->isPost()) { $form->setData($request->getPost()); if ($form->isValid()) { For a submitted form, we set the posted data to the form and check to see if it is valid using the isValid() member function of the form. The isValid() method uses the form’s input filter to test for validity and if it returns true, it will then transfer the filtered data values to the entity object that is bound to the form using the registered hydrator. This means that after isValid() is called, $task now contains the submitted form data. $this->getTaskMapper()->saveTask($task); As the form is valid, we can save $task to the database using the mapper’s saveTask() method. // Redirect to list of tasks return $this->redirect()->toRoute(’task’); After we have saved the new task, we redirect back to the list of tasks using the Redirect controller plugin. return array(’form’ => $form); Finally, if this request is not a POST, we return the variables that we want assigned to the view. In this case, just the form object. We also need to add the saveTask() method to the TaskMapper class. Open module/Checklist/src/Checklist/Model/TaskMapper.php and add this method to the end of the class: module/Checklist/src/Checklist/Model/TaskMapper.php: public function saveTask(TaskEntity $task) { $hydrator = new ClassMethods(); $data = $hydrator->extract($task); if ($task->getId()) { // update action $action = $this->sql->update(); $action->set($data); $action->where(array(’id’ => $task->getId())); } else { // insert action $action = $this->sql->insert(); unset($data[’id’]); $action->values($data); } $statement = $this->sql->prepareStatementForSqlObject($action); 58 Chapter 17. Adding new tasks
  • 99. Zend Framework 2 Documentation, Release 2.3.1dev $result = $statement->execute(); if (!$task->getId()) { $task->setId($result->getGeneratedValue()); } return $result; } The saveTask() method handles both inserting a new record if $task doesn’t have an id or updating it if it does. In either case, we need the data from the entity as an array, so we can use the hydrator to do this. If we are updating, then we use the Sql object’s update() method to create an Update object where we can set the data and a where clause. For inserting, we need an Insert object to which we set the values. Obviously, when inserting, the database will auto-increment the id, so we do not need the id property in the values list. In either case, we create a statement object and then execute it. Finally, if we are inserting, we populate the task entity’s id with the value of the auto-generated id. We now need to render the form in the add.phtml view script. Create a new PHP file called add.phtml in the module/Checklist/view/checklist/task folder and add this code: module/Checklist/view/checklist/task/add.phtml: <?php $title = ’Add new task’; $this->headTitle($title); ?> <h1><?php echo $this->escapeHtml($title); ?></h1> <?php $form = $this->form; $form->setAttribute(’action’, $this->url(’task’, array(’action’ => ’add’))); $form->get(’submit’)->setAttribute(’value’, ’Add’); $form->prepare(); echo $this->form()->openTag($form); echo $this->formHidden($form->get(’id’)); echo $this->formRow($form->get(’title’)); ?> <div> <?php echo $this->formInput($form->get(’submit’)); ?> </div> <?php echo $this->form()->closeTag($form); 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 the title element, which has a label, we can use formRow() view helper which will render the HTML for the label, the element and any validator messages that may exist. For the id and submit elements, we use formHidden() and formInput() respectively as we only need to render the element itself. We also want the submit button on its own line, so we put it within a div. Note that the formRow view helper is just a convenience - we could have used formInput(), formLabel() and formElementErrors() separately had we wanted to. If you now run the application from within Zend Studio and click the “Add new item” link from the task list page, you should see: You can now add a new task item and see it in the list of tasks. 59
  • 100. Zend Framework 2 Documentation, Release 2.3.1dev 60 Chapter 17. Adding new tasks
  • 101. CHAPTER 18 Editing a task Editing a task is almost identical to adding one, so the code is very similar. This time we use editAction() in the TaskController. Open TaskController.php and add this method to it: module/Checklist/src/Checklist/Controller/TaskController.php: public function editAction() { $id = (int)$this->params(’id’); if (!$id) { return $this->redirect()->toRoute(’task’, array(’action’=>’add’)); } $task = $this->getTaskMapper()->getTask($id); $form = new TaskForm(); $form->bind($task); $request = $this->getRequest(); if ($request->isPost()) { $form->setData($request->getPost()); if ($form->isValid()) { $this->getTaskMapper()->saveTask($task); return $this->redirect()->toRoute(’task’); } } return array( ’id’ => $id, ’form’ => $form, ); } This code should look familiar. Let’s look at the only difference from adding a task: We look for the id that is in the matched route and use it to load the task to be edited: $id = (int)$this->params(’id’); if (!$id) { return $this->redirect()->toRoute(’task’, array(’action’=>’add’)); } $task = $this->getTaskMapper()->getTask($id); The params() method is a controller plugin that provides a convenient way to retrieve parameters from the matched route. We use it to retrieve the id parameter that we defined in the task route that we created in the 61
  • 102. Zend Framework 2 Documentation, Release 2.3.1dev module.config.php. If the id is zero, then we redirect to the add action, otherwise, we continue by getting the task entity from the database. As we use the form’s bind() method with its hydrator, we do not need to populate the $task‘s data into the form manually as it will automatically be transferred for us. We also need to write a getTask() method in the TaskMapper to get a single record from the database, so let’s do that now. Open TaskMapper.php and add this method: module/Checklist/src/Checklist/Model/TaskMapper.php: public function getTask($id) { $select = $this->sql->select(); $select->where(array(’id’ => $id)); $statement = $this->sql->prepareStatementForSqlObject($select); $result = $statement->execute()->current(); if (!$result) { return null; } $hydrator = new ClassMethods(); $task = new TaskEntity(); $hydrator->hydrate($result, $task); return $task; } This method simply sets a where clause on the Sql‘s Select object and then executes it. Calling current() on the result from execute() will return either the array of data for the row or false. If we retrieved data, then we use the hydrator to populate a new TaskEntity ($task) with $data. In the same way as with the action methods, the view template, edit.phtml, looks very similar to the one for adding an task. Create a new PHP file called edit.phtml in in the module/Checklist/view/checklist/task folder and add this code: module/Checklist/view/checklist/task/edit.phtml: <?php $title = ’Edit task’; $this->headTitle($title); ?> <h1><?php echo $this->escapeHtml($title); ?></h1> <?php $form = $this->form; $url = $this->url(’task’, array(’action’ => ’edit’, ’id’ => $id)); $form->setAttribute(’action’, $url); $form->get(’submit’)->setAttribute(’value’, ’Edit’); $form->prepare(); echo $this->form()->openTag($form); echo $this->formHidden($form->get(’id’)); echo $this->formRow($form->get(’title’)); echo $this->formRow($form->get(’completed’)); ?> <div> <?php echo $this->formInput($form->get(’submit’)); ?> </div> 62 Chapter 18. Editing a task
  • 103. Zend Framework 2 Documentation, Release 2.3.1dev <?php echo $this->form()->closeTag($form); Compared to the add view script, we set the title to ‚’Edit Task’, and update the action URL to the edit action with the correct id. We also change the label of the button to ‚’edit’ and render the completed form element. You should now be able to edit tasks. 63
  • 104. Zend Framework 2 Documentation, Release 2.3.1dev 64 Chapter 18. Editing a task
  • 105. CHAPTER 19 Deleting a task To round out the core functionality of our application, we need to be able to delete a task. We have a Delete link next to each task on our list page and the naØve approach would be to run the delete action when it’s clicked. This would be wrong. Remembering the HTTP specification, we recall that you shouldn’t do an irreversible action using GET and should use POST instead. We shall therefore 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, we’ll code it directly into our view (ZendForm is, after all, optional!). Let’s start by adding the deleteAction() method to the TaskController. Open TaskController.php and add this method to it: module/Checklist/src/Checklist/Controller/TaskController.php: public function deleteAction() { $id = $this->params(’id’); $task = $this->getTaskMapper()->getTask($id); if (!$task) { return $this->redirect()->toRoute(’task’); } $request = $this->getRequest(); if ($request->isPost()) { if ($request->getPost()->get(’del’) == ’Yes’) { $this->getTaskMapper()->deleteTask($id); } return $this->redirect()->toRoute(’task’); } return array( ’id’ => $id, ’task’ => $task ); } As before, we get the id from the matched route and retrieve the task object. We then check the Request object’s isPost() to determine whether to show the confirmation page or to delete the task. We use the TaskMapper‘s deleteTask() method to delete the row and then redirect back to the list of tasks. If the request is not a POST, then we assign the task to the view, along with the id. We also need to write deleteTask(), so open TaskMapper.php and add this method: module/Checklist/src/Checklist/Model/TaskMapper.php: 65
  • 106. Zend Framework 2 Documentation, Release 2.3.1dev public function deleteTask($id) { $delete = $this->sql->delete(); $delete->where(array(’id’ => $id)); $statement = $this->sql->prepareStatementForSqlObject($delete); return $statement->execute(); } This code should look fairly familiar as we again use a Delete object from ZendDbSql and execute the statement from it. As we are using a Delete object, we set the where clause to avoid deleting every row in the table. The view script is a simple HTML form. Create a new PHP file, delete.phtml in the module/Checklist/view/checklist/task folder with this content: module/Checklist/view/checklist/task/delete.phtml: <?php $title = ’Delete task’; $this->headTitle($title); ?> <h1><?php echo $this->escapeHtml($title); ?></h1> <p>Are you sure that you want to delete the ’<?php echo $this->escapeHtml($task->title); ?>’ task? </p> <?php $url = $this->url(’task’, array(’action’ => ’delete’, ’id’=>$id)); ?> <form action="<?php echo $url; ?>" method="post"> <div> <input type="submit" name="del" value="Yes" /> <input type="submit" name="del" value="No" /> </div> </form> In this view script, we display a confirmation message and then a form with just Yes and No buttons. In the action, we checked specifically for the “Yes” value when doing the deletion. That’s it - you now have a fully working application! 66 Chapter 19. Deleting a task
  • 107. CHAPTER 20 Application Diagnostics One really useful feature of Zend Server is the code trace feature that can show you the method-by-method execution of any given PHP request. This is especially useful in a Zend Framework 2 application as the use of Events and Service Manager means that our code base isn’t necessarily linear. Let’s consider a contrived example and introduce a delay into our codebase. One of the more common causes of slow down is related to database calls taking too long due to a complicated query, incorrect indexing or by retrieving too much data. We have a very simple database table with just 5 rows, so we can simulate this by adding a sleep() call to our TaskMapper‘s fetchAll() method. Open Checklist/src/Checklist/Model/TaskMapper.php and add sleep(5); just before the end of the fetchAll() method: Checklist/src/Checklist/Model/TaskMapper.php: public function fetchAll() { $select = $this->sql->select(); $select->order(array(’completed ASC’, ’created ASC’)); $statement = $this->sql->prepareStatementForSqlObject($select); $results = $statement->execute(); $entityPrototype = new TaskEntity(); $hydrator = new ClassMethods(); $resultset = new HydratingResultSet($hydrator, $entityPrototype); $resultset->initialize($results); sleep(5); return $resultset; } It will now take 5 seconds (and a little bit) to display the list of tasks. If you now look at the home page of Zend Server’s console, you’ll see a “Slow Request Execution” critical event listed. Click on the “show” link in the “Code Trace” column as shown: You will then see much more detail about this critical event. The easiest way to use the profile view is to click on the “Statistics per Function” tab and then order by “Just own” total running time. This will result in the display of the slowest method at the top as shown in the sceenshot. As you can see, Zend Server has correctly determined that fetchAll() is the cause of the slowdown and so we can immediately go to the problem source in Zend Studio and fix the problem. 67
  • 108. Zend Framework 2 Documentation, Release 2.3.1dev In addition to helping debugging while developing, this is obviously also extremely powerful when Zend Server is running on the production servers as this profile information is then available for those situations when a given issue only seems to happen on the live web site. 68 Chapter 20. Application Diagnostics
  • 109. CHAPTER 21 Step-by-step debugging Another useful feature of Zend Studio and Eclipse/PDT is the step-by-step debugger. With the debugger you can set breakpoints in your code and then run the page in a browser. When the breakpoint is reached, Zend Studio pauses the page and you can then inspect variables and move forward through your code one line at a time. To see this in action, let’s inspect the value of $task in the checklist module’s index.phtml file. Open the mod- ule/Checklist/view/checklist/task/index.phtml file and double click in the gutter next to the opening <a tag to set a blue breakpoint marker: The break point is now set. The easiest way to run to this point is to use the Zend Studio Firefox tool bar. Once in- stalled, you can navigate to http://localhost:10088/MyTaskList/public/task in Firefox and then press the Debug button in the toolbar. Zend Studio will then come to the foreground and ask you if you want to use the Debug perspective. Answer yes, as this view is designed to provide useful information while debugging. Zend Studio will pause the application on the first line of index.php, so press F8 to continue to the breakpoint that you set. You will now see the code we are interested in. The centre pane shows our code with the line that the debugger is stopped on highlighted. The top left pane shows the stack trace which tells us which methods were used to get to this line of code. The top right pane shows a list of variables in scope. You can click the arrow next to $task to expand it and see the properties of the object. Pressing F8 will resume running the application until the next breakpoint. As our breakpoint is in a loop, it iterates once around the loop and stops again. The data in $task is now the second database record. Once you have finished inspecting the state of your code, you can press the square red stop button to stop the debugging mode. Clicking the PHP button in the top right hand corner of Zend Studio takes you back to the code editing view. 69
  • 110. Zend Framework 2 Documentation, Release 2.3.1dev 70 Chapter 21. Step-by-step debugging
  • 111. CHAPTER 22 Conclusion This concludes our brief look at building a simple, but fully functional, Zend Framework 2 application using Zend Studio 10 with the code running on Zend Server 6. It barely scratches the surface of the power and flexibility of Zend Framework and we recommend reading the manual for more information. Similarly, the combination of Zend Studio and Zend Server makes for a very powerful system for writing, debugging and deploying PHP applications. The Zend Studio manual is very helpful for getting the most out of this tool. 71
  • 112. Zend Framework 2 Documentation, Release 2.3.1dev 72 Chapter 22. Conclusion
  • 113. CHAPTER 23 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. 23.1 Installation using Composer 1. Open console (command prompt) 2. Go to your application’s directory 3. Run composer require zendframework/zftool:dev-master 23.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. 23.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. 73
  • 114. Zend Framework 2 Documentation, Release 2.3.1dev 23.4 Usage 23.4.1 From Composer or Manual install: The zf.php should be installed into the vendor/ZFTool directory (relative to your project root) - however, the command needs to be run from your project root in order for it to work correctly. You can symlink vendor/ZFTool/zf.php to your project root, or alternatively substitute zf.php for vendor/ZFTool/zf.php in the examples below. 23.4.2 Using the PHAR: Simply substitute zftool.phar for zf.php in the below examples. 23.4.3 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. 23.4.4 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. 23.4.5 Module creation > zf.php create module <name> [<path>] <name> The name of the module to be created <path> 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). 23.4.6 Classmap generator > zf.php classmap generate <directory> <classmap file> [--append|-a] [--overwrite|-w] <directory> The directory to scan for PHP classes (use "." to use current directory) <classmap file> File name for generated class map file or - for standard output. If not supplied autoload_classmap.php inside <directory>. --append | -a Append to classmap file if it exists --overwrite | -w Whether or not to overwrite existing classmap file 74 Chapter 23. Zend Framework Tool (ZFTool)
  • 115. Zend Framework 2 Documentation, Release 2.3.1dev 23.4.7 ZF library installation > zf.php install zf <path> [<version>] <path> The directory where to install the ZF2 library <version> The version to install, if not specified uses the last available 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). 23.4.8 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. 23.4. Usage 75
  • 116. Zend Framework 2 Documentation, Release 2.3.1dev 76 Chapter 23. Zend Framework Tool (ZFTool)
  • 117. CHAPTER 24 Learning Dependency Injection 24.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. 24.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 3 class A 4 { 5 /* Some useful functionality */ 6 } 7 8 class B 9 { 10 protected $a = null; 11 public function __construct(A $a) 12 { 13 $this->a = $a; 14 } 15 } 16 } To create B by hand, a developer would follow this work flow, or a similar workflow to this: 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 77
  • 118. Zend Framework 2 Documentation, Release 2.3.1dev 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 $di = new ZendDiDi; 2 $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 3 class A 4 { 5 protected $username = null; 6 protected $password = null; 7 public function __construct($username, $password) 8 { 9 $this->username = $username; 10 $this->password = $password; 11 } 12 } 13 14 class B 15 { 16 protected $a = null; 17 public function __construct(A $a) 18 { 19 $this->a = $a; 20 } 21 } 22 23 class C 24 { 25 protected $b = null; 26 public function __construct(B $b) 27 { 28 $this->b = $b; 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 $di = new ZendDiDi; 2 $di->getInstanceManager()->setProperty(’A’, ’username’, ’MyUsernameValue’); 3 $di->getInstanceManager()->setProperty(’A’, ’password’, ’MyHardToGuessPassword%$#’); 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: 78 Chapter 24. Learning Dependency Injection
  • 119. Zend Framework 2 Documentation, Release 2.3.1dev 1 $c = $di->get(’MyC’); 2 // or 3 $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 $parameters = array( 2 ’username’ => ’MyUsernameValue’, 3 ’password’ => ’MyHardToGuessPassword%$#’, 4 ); 5 6 $c = $di->get(’MyC’, $parameters); 7 // or 8 $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 namespace My { 2 class B 3 { 4 protected $a; 5 public function setA(A $a) 6 { 7 $this->a = $a; 8 } 9 } 10 } 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. 24.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 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: 24.3. Simplest Usage Case Without Type-hints 79
  • 120. Zend Framework 2 Documentation, Release 2.3.1dev 1 namespace My { 2 class B 3 { 4 protected $a; 5 public function setA($a) 6 { 7 $this->a = $a; 8 } 9 } 10 } You’ll notice the only change is that setA now does not include any type-hinting information. 1 use ZendDiDi; 2 use ZendDiDefinition; 3 use ZendDiDefinitionBuilder; 4 5 // Describe this class: 6 $builder = new DefinitionBuilderDefinition; 7 $builder->addClass(($class = new BuilderPhpClass)); 8 9 $class->setName(’MyB’); 10 $class->addInjectableMethod(($im = new BuilderInjectableMethod)); 11 12 $im->setName(’setA’); 13 $im->addParameter(’a’, ’MyA’); 14 15 // Use both our Builder Definition as well as the default 16 // RuntimeDefinition, builder first 17 $aDef = new DefinitionAggregateDefinition; 18 $aDef->addDefinition($builder); 19 $aDef->addDefinition(new DefinitionRuntimeDefinition); 20 21 // Now make sure the Di understands it 22 $di = new Di; 23 $di->setDefinition($aDef); 24 25 // and finally, create C 26 $parameters = array( 27 ’username’ => ’MyUsernameValue’, 28 ’password’ => ’MyHardToGuessPassword%$#’, 29 ); 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. 24.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 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 80 Chapter 24. Learning Dependency Injection
  • 121. Zend Framework 2 Documentation, Release 2.3.1dev 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 $compiler = new ZendDiDefinitionCompiler(); 2 $compiler->addCodeScannerDirectory( 3 new ZendCodeScannerScannerDirectory(’path/to/library/My/’) 4 ); 5 $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 $di = new ZendDiDi; 2 $di->setDefinition($definition); 3 $di->getInstanceManager()->setProperty(’MyA’, ’username’, ’foo’); 4 $di->getInstanceManager()->setProperty(’MyA’, ’password’, ’bar’); 5 $c = $di->get(’MyC’); One strategy for persisting these compiled definitions would be the following: 1 if (!file_exists(__DIR__ . ’/di-definition.php’) && $isProduction) { 2 $compiler = new ZendDiDefinitionCompiler(); 3 $compiler->addCodeScannerDirectory( 4 new ZendCodeScannerScannerDirectory(’path/to/library/My/’) 5 ); 6 $definition = $compiler->compile(); 7 file_put_contents( 8 __DIR__ . ’/di-definition.php’, 9 ’<?php return ’ . var_export($definition->toArray(), true) . ’;’ 10 ); 11 } else { 12 $definition = new ZendDiDefinitionArrayDefinition( 13 include __DIR__ . ’/di-definition.php’ 14 ); 15 } 16 24.4. Simplest usage case with Compiled Definition 81
  • 122. Zend Framework 2 Documentation, Release 2.3.1dev 17 // $definition can now be used; in a production system it will be written 18 // to disk. Since ZendCodeScanner does not include files, the classes contained within are not loaded into memory. In- stead, 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. 24.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 // First, compile the information 2 $compiler = new ZendDiDefinitionCompilerDefinition(); 3 $compiler->addDirectoryScanner( 4 new ZendCodeScannerDirectoryScanner(__DIR__ . ’/My/’) 5 ); 6 $compiler->compile(); 7 $definition = $compiler->toArrayDefinition(); 8 9 // Now, create a Definition class for this information 10 $codeGenerator = new ZendCodeGeneratorFileGenerator(); 11 $codeGenerator->setClass(($class = new ZendCodeGeneratorClassGenerator())); 12 $class->setNamespaceName(’My’); 13 $class->setName(’DiDefinition’); 14 $class->setExtendedClass(’ZendDiDefinitionArrayDefinition’); 15 $class->addMethod( 16 ’__construct’, 17 array(), 18 ZendCodeGeneratorMethodGenerator::FLAG_PUBLIC, 19 ’parent::__construct(’ . var_export($definition->toArray(), true) . ’);’ 20 ); 21 file_put_contents(__DIR__ . ’/My/DiDefinition.php’, $codeGenerator->generate()); 24.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 use ZendDiDi; 2 use ZendDiDefinition; 3 use ZendDiDefinitionBuilder; 4 5 $di = new Di; 6 $diDefAggregate = new DefinitionAggregate(); 7 8 // first add in provided Definitions, for example 9 $diDefAggregate->addDefinition(new ThirdPartyDbalDiDefinition()); 10 $diDefAggregate->addDefinition(new ZendControllerDiDefinition()); 11 82 Chapter 24. Learning Dependency Injection
  • 123. Zend Framework 2 Documentation, Release 2.3.1dev 12 // for code that does not have TypeHints 13 $builder = new DefinitionBuilderDefinition(); 14 $builder->addClass(($class = BuilderPhpClass)); 15 $class->addInjectionMethod( 16 ($injectMethod = new BuilderInjectionMethod()) 17 ); 18 $injectMethod->setName(’injectImplementation’); 19 $injectMethod->addParameter( 20 ’implementation’, ’ClassForSpecificImplementation’ 21 ); 22 23 // now, your application code 24 $compiler = new DefinitionCompiler() 25 $compiler->addCodeScannerDirectory( 26 new ZendCodeScannerDirectoryScanner(__DIR__ . ’/App/’) 27 ); 28 $appDefinition = $compiler->compile(); 29 $diDefAggregate->addDefinition($appDefinition); 30 31 // now, pass in properties 32 $im = $di->getInstanceManager(); 33 34 // this could come from ZendConfigConfig::toArray 35 $propertiesFromConfig = array( 36 ’ThirdPartyDbalDbAdapter’ => array( 37 ’username’ => ’someUsername’, 38 ’password’ => ’somePassword’ 39 ), 40 ’ZendControllerHelperContentType’ => array( 41 ’default’ => ’xhtml5’ 42 ), 43 ); 44 $im->setProperties($propertiesFromConfig); 24.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 // $di is a fully configured DI instance 4 $generator = new Generator($di); 5 6 $generator->setNamespace(’Application’) 24.7. Generating Service Locators 83
  • 124. Zend Framework 2 Documentation, Release 2.3.1dev 7 ->setContainerClass(’Context’); 8 $file = $generator->getCodeGenerator(); 9 $file->setFilename(__DIR__ . ’/../Application/Context.php’); 10 $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 class Context extends ServiceLocator 8 { 9 10 public function get($name, array $params = array()) 11 { 12 switch ($name) { 13 case ’composed’: 14 case ’MyComposedClass’: 15 return $this->getMyComposedClass(); 16 17 case ’struct’: 18 case ’MyStruct’: 19 return $this->getMyStruct(); 20 21 default: 22 return parent::get($name, $params); 23 } 24 } 25 26 public function getComposedClass() 27 { 28 if (isset($this->services[’MyComposedClass’])) { 29 return $this->services[’MyComposedClass’]; 30 } 31 32 $object = new MyComposedClass(); 33 $this->services[’MyComposedClass’] = $object; 34 return $object; 35 } 36 public function getMyStruct() 37 { 38 if (isset($this->services[’MyStruct’])) { 39 return $this->services[’MyStruct’]; 40 } 41 42 $object = new MyStruct(); 43 $this->services[’MyStruct’] = $object; 44 return $object; 45 } 46 47 public function getComposed() 48 { 49 return $this->get(’MyComposedClass’); 50 } 84 Chapter 24. Learning Dependency Injection
  • 125. Zend Framework 2 Documentation, Release 2.3.1dev 51 52 public function getStruct() 53 { 54 return $this->get(’MyStruct’); 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. 24.7. Generating Service Locators 85
  • 126. Zend Framework 2 Documentation, Release 2.3.1dev 86 Chapter 24. Learning Dependency Injection
  • 127. CHAPTER 25 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 in- volved. Going back and manually testing every individual component of an application after every change is imprac- tical. 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.* 25.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. 25.2 Bootstrapping your tests Next, create a file called phpunit.xml under zf2-tutorial/module/Album/test: 87
  • 128. Zend Framework 2 Documentation, Release 2.3.1dev <?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 use ZendLoaderAutoloaderFactory; 6 use ZendMvcServiceServiceManagerConfig; 7 use ZendServiceManagerServiceManager; 8 use RuntimeException; 9 10 error_reporting(E_ALL | E_STRICT); 11 chdir(__DIR__); 12 13 /** 14 * Test bootstrap, for setting up autoloading 15 */ 16 class Bootstrap 17 { 18 protected static $serviceManager; 19 20 public static function init() 21 { 22 $zf2ModulePaths = array(dirname(dirname(__DIR__))); 23 if (($path = static::findParentPath(’vendor’))) { 24 $zf2ModulePaths[] = $path; 25 } 26 if (($path = static::findParentPath(’module’)) !== $zf2ModulePaths[0]) { 27 $zf2ModulePaths[] = $path; 28 } 29 30 static::initAutoloader(); 31 32 // use ModuleManager to load this module and it’s dependencies 33 $config = array( 34 ’module_listener_options’ => array( 35 ’module_paths’ => $zf2ModulePaths, 36 ), 37 ’modules’ => array( 38 ’Album’ 39 ) 40 ); 41 42 $serviceManager = new ServiceManager(new ServiceManagerConfig()); 43 $serviceManager->setService(’ApplicationConfig’, $config); 44 $serviceManager->get(’ModuleManager’)->loadModules(); 45 static::$serviceManager = $serviceManager; 46 } 88 Chapter 25. Unit Testing a Zend Framework 2 application
  • 129. Zend Framework 2 Documentation, Release 2.3.1dev 47 48 public static function chroot() 49 { 50 $rootPath = dirname(static::findParentPath(’module’)); 51 chdir($rootPath); 52 } 53 54 public static function getServiceManager() 55 { 56 return static::$serviceManager; 57 } 58 59 protected static function initAutoloader() 60 { 61 $vendorPath = static::findParentPath(’vendor’); 62 63 $zf2Path = getenv(’ZF2_PATH’); 64 if (!$zf2Path) { 65 if (defined(’ZF2_PATH’)) { 66 $zf2Path = ZF2_PATH; 67 } elseif (is_dir($vendorPath . ’/ZF2/library’)) { 68 $zf2Path = $vendorPath . ’/ZF2/library’; 69 } elseif (is_dir($vendorPath . ’/zendframework/zendframework/library’)) { 70 $zf2Path = $vendorPath . ’/zendframework/zendframework/library’; 71 } 72 } 73 74 if (!$zf2Path) { 75 throw new RuntimeException( 76 ’Unable to load ZF2. Run ‘php composer.phar install‘ or’ 77 . ’ define a ZF2_PATH environment variable.’ 78 ); 79 } 80 81 if (file_exists($vendorPath . ’/autoload.php’)) { 82 include $vendorPath . ’/autoload.php’; 83 } 84 85 include $zf2Path . ’/Zend/Loader/AutoloaderFactory.php’; 86 AutoloaderFactory::factory(array( 87 ’ZendLoaderStandardAutoloader’ => array( 88 ’autoregister_zf’ => true, 89 ’namespaces’ => array( 90 __NAMESPACE__ => __DIR__ . ’/’ . __NAMESPACE__, 91 ), 92 ), 93 )); 94 } 95 96 protected static function findParentPath($path) 97 { 98 $dir = __DIR__; 99 $previousDir = ’.’; 100 while (!is_dir($dir . ’/’ . $path)) { 101 $dir = dirname($dir); 102 if ($previousDir === $dir) { 103 return false; 104 } 25.2. Bootstrapping your tests 89
  • 130. Zend Framework 2 Documentation, Release 2.3.1dev 105 $previousDir = $dir; 106 } 107 return $dir . ’/’ . $path; 108 } 109 } 110 111 Bootstrap::init(); 112 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. 25.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 AlbumControllerTest.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(); } } 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. 90 Chapter 25. Unit Testing a Zend Framework 2 application
  • 131. Zend Framework 2 Documentation, Release 2.3.1dev 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. 25.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; 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. 25.4. A failing test case 91
  • 132. Zend Framework 2 Documentation, Release 2.3.1dev 25.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 fol- lows: 1 public function testIndexActionCanBeAccessed() 2 { 3 $albumTableMock = $this->getMockBuilder(’AlbumModelAlbumTable’) 4 ->disableOriginalConstructor() 5 ->getMock(); 6 7 $albumTableMock->expects($this->once()) 8 ->method(’fetchAll’) 9 ->will($this->returnValue(array())); 10 11 $serviceManager = $this->getApplicationServiceLocator(); 12 $serviceManager->setAllowOverride(true); 13 $serviceManager->setService(’AlbumModelAlbumTable’, $albumTableMock); 14 15 $this->dispatch(’/album’); 16 $this->assertResponseStatusCode(200); 17 18 $this->assertModuleName(’Album’); 19 $this->assertControllerName(’AlbumControllerAlbum’); 20 $this->assertControllerClass(’AlbumController’); 21 $this->assertMatchedRouteName(’album’); 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 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 . 92 Chapter 25. Unit Testing a Zend Framework 2 application
  • 133. Zend Framework 2 Documentation, Release 2.3.1dev Time: 0 seconds, Memory: 9.00Mb OK (1 test, 6 assertions) 25.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 .. 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. When testing the editAction you will also need to mock out the getAlbum method: $albumTableMock->expects($this->once()) ->method(’getAlbum’) ->will($this->returnValue(new AlbumModelAlbum())); 25.6. Testing actions with POST 93
  • 134. Zend Framework 2 Documentation, Release 2.3.1dev 25.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 <?php 2 namespace AlbumTestModel; 3 4 use AlbumModelAlbum; 5 use PHPUnit_Framework_TestCase; 6 7 class AlbumTest extends PHPUnit_Framework_TestCase 8 { 9 public function testAlbumInitialState() 10 { 11 $album = new Album(); 12 13 $this->assertNull( 14 $album->artist, 15 ’"artist" should initially be null’ 16 ); 17 $this->assertNull( 18 $album->id, 19 ’"id" should initially be null’ 20 ); 21 $this->assertNull( 22 $album->title, 23 ’"title" should initially be null’ 24 ); 25 } 26 27 public function testExchangeArraySetsPropertiesCorrectly() 28 { 29 $album = new Album(); 30 $data = array(’artist’ => ’some artist’, 31 ’id’ => 123, 32 ’title’ => ’some title’); 33 34 $album->exchangeArray($data); 35 36 $this->assertSame( 37 $data[’artist’], 38 $album->artist, 39 ’"artist" was not set correctly’ 40 ); 41 $this->assertSame( 42 $data[’id’], 43 $album->id, 44 ’"id" was not set correctly’ 45 ); 46 $this->assertSame( 47 $data[’title’], 94 Chapter 25. Unit Testing a Zend Framework 2 application
  • 135. Zend Framework 2 Documentation, Release 2.3.1dev 48 $album->title, 49 ’"title" was not set correctly’ 50 ); 51 } 52 53 public function testExchangeArraySetsPropertiesToNullIfKeysAreNotPresent() 54 { 55 $album = new Album(); 56 57 $album->exchangeArray(array(’artist’ => ’some artist’, 58 ’id’ => 123, 59 ’title’ => ’some title’)); 60 $album->exchangeArray(array()); 61 62 $this->assertNull( 63 $album->artist, ’"artist" should have defaulted to null’ 64 ); 65 $this->assertNull( 66 $album->id, ’"id" should have defaulted to null’ 67 ); 68 $this->assertNull( 69 $album->title, ’"title" should have defaulted to null’ 70 ); 71 } 72 73 public function testGetArrayCopyReturnsAnArrayWithPropertyValues() 74 { 75 $album = new Album(); 76 $data = array(’artist’ => ’some artist’, 77 ’id’ => 123, 78 ’title’ => ’some title’); 79 80 $album->exchangeArray($data); 81 $copyArray = $album->getArrayCopy(); 82 83 $this->assertSame( 84 $data[’artist’], 85 $copyArray[’artist’], 86 ’"artist" was not set correctly’ 87 ); 88 $this->assertSame( 89 $data[’id’], 90 $copyArray[’id’], 91 ’"id" was not set correctly’ 92 ); 93 $this->assertSame( 94 $data[’title’], 95 $copyArray[’title’], 96 ’"title" was not set correctly’ 97 ); 98 } 99 100 public function testInputFiltersAreSetCorrectly() 101 { 102 $album = new Album(); 103 104 $inputFilter = $album->getInputFilter(); 105 25.7. Testing model entities 95
  • 136. Zend Framework 2 Documentation, Release 2.3.1dev 106 $this->assertSame(3, $inputFilter->count()); 107 $this->assertTrue($inputFilter->has(’artist’)); 108 $this->assertTrue($inputFilter->has(’id’)); 109 $this->assertTrue($inputFilter->has(’title’)); 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) 25.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 con- tents: <?php namespace AlbumTestModel; use AlbumModelAlbumTable; use AlbumModelAlbum; use ZendDbResultSetResultSet; use PHPUnit_Framework_TestCase; class AlbumTableTest extends PHPUnit_Framework_TestCase { public function testFetchAllReturnsAllAlbums() { $resultSet = new ResultSet(); $mockTableGateway = $this->getMock( ’ZendDbTableGatewayTableGateway’, array(’select’), 96 Chapter 25. Unit Testing a Zend Framework 2 application
  • 137. Zend Framework 2 Documentation, Release 2.3.1dev 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)); } public function testCanDeleteAnAlbumByItsId() { $mockTableGateway = $this->getMock( ’ZendDbTableGatewayTableGateway’, array(’delete’), array(), ’’, false 25.8. Testing model tables 97
  • 138. Zend Framework 2 Documentation, Release 2.3.1dev ); $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’), array(), ’’, false ); $mockTableGateway->expects($this->once()) ->method(’select’) ->with(array(’id’ => 123)) ->will($this->returnValue($resultSet)); 98 Chapter 25. Unit Testing a Zend Framework 2 application
  • 139. Zend Framework 2 Documentation, Release 2.3.1dev $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. 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: 25.8. Testing model tables 99
  • 140. Zend Framework 2 Documentation, Release 2.3.1dev 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) 25.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. 100 Chapter 25. Unit Testing a Zend Framework 2 application
  • 141. CHAPTER 26 Using the EventManager This tutorial explores the various features of ZendEventManager. 26.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. 26.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 $events = new EventManager(); 4 $events->attach(’do’, function ($e) { 5 $event = $e->getName(); 6 $params = $e->getParams(); 7 printf( 8 ’Handled event "%s", with parameters %s’, 9 $event, 10 json_encode($params) 11 ); 12 }); 13 14 $params = array(’foo’ => ’bar’, ’baz’ => ’bat’); 15 $events->trigger(’do’, null, $params); 101
  • 142. Zend Framework 2 Documentation, Release 2.3.1dev 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 use ZendEventManagerEventManager; 2 use ZendEventManagerEventManagerAwareInterface; 3 use ZendEventManagerEventManagerInterface; 4 5 class Example implements EventManagerAwareInterface 6 { 7 protected $events; 8 9 public function setEventManager(EventManagerInterface $events) 10 { 11 $events->setIdentifiers(array( 12 __CLASS__, 13 get_class($this) 14 )); 15 $this->events = $events; 16 } 17 18 public function getEventManager() 19 { 20 if (!$this->events) { 21 $this->setEventManager(new EventManager()); 22 } 23 return $this->events; 24 } 25 26 public function do($foo, $baz) 27 { 28 $params = compact(’foo’, ’baz’); 29 $this->getEventManager()->trigger(__FUNCTION__, $this, $params); 30 } 31 32 } 33 34 $example = new Example(); 35 36 $example->getEventManager()->attach(’do’, function($e) { 37 $event = $e->getName(); 38 $target = get_class($e->getTarget()); // "Example" 39 $params = $e->getParams(); 40 printf( 41 ’Handled event "%s" on target "%s", with parameters %s’, 42 $event, 43 $target, 44 json_encode($params) 102 Chapter 26. Using the EventManager
  • 143. Zend Framework 2 Documentation, Release 2.3.1dev 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 ar- gument 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? 26.3 Shared managers One aspect that the EventManager implementation provides is an ability to compose a SharedEventManagerInterface implementation. ZendEventManagerSharedEventManagerInterface describes an object that aggregates listeners for events attached to objects with specific identifiers. It does not trigger events itself. Instead, an EventManager in- stance 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 $sharedEvents = new SharedEventManager(); 4 $sharedEvents->attach(’Example’, ’do’, function ($e) { 5 $event = $e->getName(); 6 $target = get_class($e->getTarget()); // "Example" 7 $params = $e->getParams(); 8 printf( 9 ’Handled event "%s" on target "%s", with parameters %s’, 10 $event, 11 $target, 12 json_encode($params) 13 ); 14 }); 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 $example = new Example(); 2 $example->getEventManager()->setSharedManager($sharedEvents); 3 $example->do(’bar’, ’bat’); and expect the following to be echo‘d: 26.3. Shared managers 103
  • 144. Zend Framework 2 Documentation, Release 2.3.1dev Handled event "do" on target "Example", with parameters {"foo":"bar","baz":"bat"} Now, let’s say we extended Example as follows: 1 class SubExample extends Example 2 { 3 } 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); 104 Chapter 26. Using the EventManager
  • 145. CHAPTER 27 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 // Multiple named events: 2 $events->attach( 3 array(’foo’, ’bar’, ’baz’), // events 4 $listener 5 ); 6 7 // All events via wildcard: 8 $events->attach( 9 ’*’, // all events 10 $listener 11 ); 12 13 // Multiple named targets: 14 $sharedEvents->attach( 15 array(’Foo’, ’Bar’, ’Baz’), // targets 16 ’doSomething’, // named event 17 $listener 18 ); 19 20 // All targets via wildcard 21 $sharedEvents->attach( 22 ’*’, // all targets 23 ’doSomething’, // named event 24 $listener 25 ); 26 27 // Mix and match: multiple named events on multiple named targets: 28 $sharedEvents->attach( 29 array(’Foo’, ’Bar’, ’Baz’), // targets 30 array(’foo’, ’bar’, ’baz’), // events 31 $listener 32 ); 33 34 // Mix and match: all events on multiple named targets: 35 $sharedEvents->attach( 105
  • 146. Zend Framework 2 Documentation, Release 2.3.1dev 36 array(’Foo’, ’Bar’, ’Baz’), // targets 37 ’*’, // events 38 $listener 39 ); 40 41 // Mix and match: multiple named events on all targets: 42 $sharedEvents->attach( 43 ’*’, // targets 44 array(’foo’, ’bar’, ’baz’), // events 45 $listener 46 ); 47 48 // Mix and match: all events on all targets: 49 $sharedEvents->attach( 50 ’*’, // targets 51 ’*’, // events 52 $listener 53 ); The ability to specify multiple targets and/or events when attaching can slim down your code immensely. 106 Chapter 27. Wildcards
  • 147. CHAPTER 28 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 mul- tiple 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 use ZendEventManagerEventInterface; 2 use ZendEventManagerEventManagerInterface; 3 use ZendEventManagerListenerAggregateInterface; 4 use ZendLogLogger; 5 6 class LogEvents implements ListenerAggregateInterface 7 { 8 protected $listeners = array(); 9 protected $log; 10 11 public function __construct(Logger $log) 12 { 13 $this->log = $log; 14 } 15 16 public function attach(EventManagerInterface $events) 17 { 18 $this->listeners[] = $events->attach(’do’, array($this, ’log’)); 19 $this->listeners[] = $events->attach(’doSomethingElse’, array($this, ’log’)); 20 } 21 22 public function detach(EventCollection $events) 23 { 24 foreach ($this->listeners as $index => $listener) { 25 if ($events->detach($listener)) { 26 unset($this->listeners[$index]; 27 } 28 } 29 } 30 31 public function log(EventInterface $e) 32 { 33 $event = $e->getName(); 107
  • 148. Zend Framework 2 Documentation, Release 2.3.1dev 34 $params = $e->getParams(); 35 $this->log->info(sprintf(’%s: %s’, $event, json_encode($params))); 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. 28.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 in- stance. 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. 28.2 Short-circuiting 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. Here’s an example: 108 Chapter 28. Listener aggregates
  • 149. Zend Framework 2 Documentation, Release 2.3.1dev 1 public function someExpensiveCall($criteria1, $criteria2) 2 { 3 $params = compact(’criteria1’, ’criteria2’); 4 $results = $this->getEventManager()->trigger( 5 __FUNCTION__, 6 $this, 7 $params, 8 function ($r) { 9 return ($r instanceof SomeResultClass); 10 } 11 ); 12 if ($results->stopped()) { 13 return $results->last(); 14 } 15 16 // ... do some work ... 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 $events->attach(’do’, function ($e) { 2 $e->stopPropagation(); 3 return new SomeResultClass(); 4 }); 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. 28.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 SharedEventManager::attach() accept one additional argu- ment, 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 $priority = 100; 2 $events->attach(’Example’, ’do’, function($e) { 3 $event = $e->getName(); 4 $target = get_class($e->getTarget()); // "Example" 5 $params = $e->getParams(); 6 printf( 7 ’Handled event "%s" on target "%s", with parameters %s’, 8 $event, 9 $target, 10 json_encode($params) 28.3. Keeping it in order 109
  • 150. Zend Framework 2 Documentation, Release 2.3.1dev 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 neces- sary in order to set appropriate priority values. We advise avoiding setting a priority value unless absolutely necessary. 28.4 Custom event objects Hopefully some of you have been wondering, “where and when is the Event object created”? In all of the exam- ples 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 $routeMatch = $e->getParam(’route-match’, false); 2 if (!$routeMatch) { 3 // Oh noes! we cannot do our work! whatever shall we do?!?!?! 4 } 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 // in the method: 2 $params[’__RESULT’] = $computedResult; 3 $events->trigger(__FUNCTION__ . ’.post’, $this, $params); 4 5 // in the listener: 6 $result = $e->getParam(’__RESULT__’); 7 if (!$result) { 8 // Oh noes! we cannot do our work! whatever shall we do?!?!?! 9 } 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 $response = $e->getResponse(); 2 $result = $e->getResult(); 3 if (is_string($result)) { 4 $content = $view->render(’layout.phtml’, array(’content’ => $result)); 5 $response->setContent($content); 6 } 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. 110 Chapter 28. Listener aggregates
  • 151. Zend Framework 2 Documentation, Release 2.3.1dev 1 $event = new CustomEvent(); 2 $event->setSomeKey($value); 3 4 // Injected with event name and target: 5 $events->trigger(’foo’, $this, $event); 6 7 // Injected with event name: 8 $event->setTarget($this); 9 $events->trigger(’foo’, $event); 10 11 // Fully encapsulates all necessary properties: 12 $event->setName(’foo’); 13 $event->setTarget($this); 14 $events->trigger($event); 15 16 // Passing a callback following the event object works for 17 // short-circuiting, too. 18 $results = $events->trigger(’foo’, $this, $event, $callback); This is a really powerful technique for domain-specific event systems, and definitely worth experimenting with. 28.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 public function someExpensiveCall($criteria1, $criteria2) 2 { 3 $params = compact(’criteria1’, ’criteria2’); 4 $results = $this->getEventManager()->trigger( 5 __FUNCTION__ . ’.pre’, 6 $this, 7 $params, 8 function ($r) { 9 return ($r instanceof SomeResultClass); 10 } 11 ); 12 if ($results->stopped()) { 13 return $results->last(); 14 } 15 16 // ... do some work ... 28.5. Putting it together: Implementing a simple caching system 111
  • 152. Zend Framework 2 Documentation, Release 2.3.1dev 17 18 $params[’__RESULT__’] = $calculatedResult; 19 $this->events()->trigger(__FUNCTION__ . ’.post’, $this, $params); 20 return $calculatedResult; 21 } Now, to provide some caching listeners. We’ll need to attach to each of the “someExpensiveCall.pre” and “someEx- pensiveCall.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 $events->attach(’someExpensiveCall.pre’, function($e) use ($cache) { 2 $params = $e->getParams(); 3 $key = md5(json_encode($params)); 4 $hit = $cache->load($key); 5 return $hit; 6 }, 100); 7 8 $events->attach(’someExpensiveCall.post’, function($e) use ($cache) { 9 $params = $e->getParams(); 10 $result = $params[’__RESULT__’]; 11 unset($params[’__RESULT__’]); 12 $key = md5(json_encode($params)); 13 $cache->save($result, $key); 14 }, -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 public function setEventManager(EventManagerInterface $events) 2 { 3 $this->events = $events; 4 $events->setIdentifiers(array(__CLASS__, get_class($this))); 5 $events->attach(’someExpensiveCall’, array($this, ’doSomeExpensiveCall’)); 6 } 7 8 public function someExpensiveCall($criteria1, $criteria2) 9 { 10 $params = compact(’criteria1’, ’criteria2’); 11 $results = $this->getEventManager()->trigger( 12 __FUNCTION__, 13 $this, 14 $params, 15 function ($r) { 16 return ($r instanceof SomeResultClass); 17 } 18 ); 19 return $results->last(); 20 } 21 22 public function doSomeExpensiveCall($e) 112 Chapter 28. Listener aggregates
  • 153. Zend Framework 2 Documentation, Release 2.3.1dev 23 { 24 // ... do some work ... 25 $e->setParam(’__RESULT__’, $calculatedResult); 26 return $calculatedResult; 27 } 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. 28.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. 28.6. Conclusion 113
  • 154. Zend Framework 2 Documentation, Release 2.3.1dev 114 Chapter 28. Listener aggregates
  • 155. CHAPTER 29 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 con- figuration globbed in specified directories (typically config/autoload/). • Finally, immediately prior to the merged application configuration being passed to the ServiceManager, it is passed to a special EVENT_MERGE_CONFIG event to allow further modification. In this tutorial, we’ll look at the exact sequence, and how you can tie into it. 29.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 configura- tion 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 is by default in config/application.config.php. The defaults look like this: 1 <?php 2 return array( 3 // This should be an array of module namespaces used in the application. 4 ’modules’ => array( 5 ’Application’, 6 ), 7 8 // These are various options for the listeners attached to the ModuleManager 9 ’module_listener_options’ => array( 10 // This should be an array of paths in which modules reside. 11 // If a string key is provided, the listener will consider that a module 12 // namespace, the value of that key the specific path to that module’s 13 // Module class. 14 ’module_paths’ => array( 15 ’./module’, 16 ’./vendor’, 115
  • 156. Zend Framework 2 Documentation, Release 2.3.1dev 17 ), 18 19 // An array of paths from which to glob configuration files after 20 // modules are loaded. These effectively overide configuration 21 // provided by modules themselves. Paths may use GLOB_BRACE notation. 22 ’config_glob_paths’ => array( 23 ’config/autoload/{,*.}{global,local}.php’, 24 ), 25 26 // Whether or not to enable a configuration cache. 27 // If enabled, the merged configuration will be cached and used in 28 // subsequent requests. 29 //’config_cache_enabled’ => $booleanValue, 30 31 // The key used to create the configuration cache file name. 32 //’config_cache_key’ => $stringKey, 33 34 // Whether or not to enable a module class map cache. 35 // If enabled, creates a module class map cache which will be used 36 // by in future requests, to reduce the autoloading process. 37 //’module_map_cache_enabled’ => $booleanValue, 38 39 // The key used to create the class map cache file name. 40 //’module_map_cache_key’ => $stringKey, 41 42 // The path in which to cache merged configuration. 43 //’cache_dir’ => $stringPath, 44 45 // Whether or not to enable modules dependency checking. 46 // Enabled by default, prevents usage of modules that depend on other modules 47 // that weren’t loaded. 48 // ’check_dependencies’ => true, 49 ), 50 51 // Used to create an own service manager. May contain one or more child arrays. 52 //’service_listener_options’ => array( 53 // array( 54 // ’service_manager’ => $stringServiceManagerName, 55 // ’config_key’ => $stringConfigKey, 56 // ’interface’ => $stringOptionalInterface, 57 // ’method’ => $stringRequiredMethodName, 58 // ), 59 // ) 60 61 // Initial configuration with which to seed the ServiceManager. 62 // Should be compatible with ZendServiceManagerConfig. 63 // ’service_manager’ => array(), 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, with the exception of the values under the ‘service_manager’ key, it cannot be overridden by a module. This leads us to our first trick: how do you provide environment-specific system configuration? 116 Chapter 29. Advanced Configuration Tricks
  • 157. Zend Framework 2 Documentation, Release 2.3.1dev 29.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 <?php 2 $env = getenv(’APP_ENV’) ?: ’production’; 3 4 // Use the $env value to determine which modules to load 5 $modules = array( 6 ’Application’, 7 ); 8 if ($env == ’development’) { 9 $modules[] = ’ZendDeveloperTools’; 10 } 11 12 return array( 13 ’modules’ => $modules, 14 15 ’module_listener_options’ => array( 16 ’module_paths’ => array( 17 ’./module’, 18 ’./vendor’, 19 ), 20 21 ’config_glob_paths’ => array( 22 ’config/autoload/{,*.}{global,local}.php’, 23 ), 24 25 // Use the $env value to determine the state of the flag 26 ’config_cache_enabled’ => ($env == ’production’), 27 28 ’config_cache_key’ => ’app_config’, 29 30 // Use the $env value to determine the state of the flag 31 ’module_map_cache_enabled’ => ($env == ’production’), 32 33 ’module_map_cache_key’ => ’module_map’, 34 35 ’cache_dir’ => ’data/config/’, 29.1. System configuration 117
  • 158. Zend Framework 2 Documentation, Release 2.3.1dev 36 37 // Use the $env value to determine the state of the flag 38 ’check_dependencies’ => ($env != ’production’), 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? 29.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 ser- vice 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 configura- tion – 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 devel- opment 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: 118 Chapter 29. Advanced Configuration Tricks
  • 159. Zend Framework 2 Documentation, Release 2.3.1dev 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). 29.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. 29.2. Module Configuration 119
  • 160. Zend Framework 2 Documentation, Release 2.3.1dev 29.3 Configuration mapping table Manager name Interface name Module Method name Config key name ControllerPluginManagerControllerPluginProviderInterfacegetControllerPluginConfig()controller_plugins ControllerManager ControllerProviderInterfacegetControllerConfig()controllers FilterManager FilterProviderInterface getFilterConfig() filters FormElementManager FormElementProviderInterfacegetFormElementConfig()form_elements HydratorManager HydratorProviderInterfacegetHydratorConfig() hydrators InputFilterManager InputFilterProviderInterfacegetInputFilterConfig()input_filters RoutePluginManager RouteProviderInterface getRouteConfig() route_manager SerializerAdapterManagerSerializerProviderInterfacegetSerializerConfig()serializers ServiceLocator ServiceProviderInterface getServiceConfig() service_manager ValidatorManager ValidatorProviderInterfacegetValidatorConfig() validators ViewHelperManager ViewHelperProviderInterfacegetViewHelperConfig()view_helpers LogProcessorManagerLogProcessorProviderInterfacegetLogProcessorConfiglog_processors LogWriterManager LogWriterProviderInterfacegetLogWriterConfig log_writers 29.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 the various service configuration methods in a module class • configuration returned by getConfig() In other words, your getConfig() win over the various service configuration methods. Additionally, and of partic- ular note: the configuration returned from those methods will not be cached. 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 con- figuration files in other markup formats. 29.5 Manipulating merged configuration Occasionally you will want to not just override an application configuration key, but actually remove it. Since merging will not remove keys, how can you handle this? ZendModuleManagerListenerConfigListener triggers a special event, ZendModuleManagerModuleEvent::EVENT_MERGE_CONFIG, after merging all configuration, but prior to it being passed to the ServiceManager. By listening to this event, you can inspect the merged configuration and manipulate it. The ConfigListener itself listens to the event at priority 1000 (i.e., very high), which is when the configuration is merged. You can tie into this to modify the merged configuration from your module, via the init() method. 1 namespace Foo; 2 3 use ZendModuleManagerModuleEvent; 4 use ZendModuleManagerModuleManager; 5 120 Chapter 29. Advanced Configuration Tricks
  • 161. Zend Framework 2 Documentation, Release 2.3.1dev 6 class Module 7 { 8 public function init(ModuleManager $moduleManager) 9 { 10 $events = $moduleManager->getEventManager(); 11 12 // Registering a listener at default priority, 1, which will trigger 13 // after the ConfigListener merges config. 14 $events->attach(ModuleEvent::EVENT_MERGE_CONFIG, array($this, ’onMergeConfig’)); 15 } 16 17 public function onMergeConfig(ModuleEvent $e) 18 { 19 $configListener = $e->getConfigListener(); 20 $config = $configListener->getMergedConfig(false); 21 22 // Modify the configuration; here, we’ll remove a specific key: 23 if (isset($config[’some_key’])) { 24 unset($config[’some_key’]); 25 } 26 27 // Pass the changed configuration back to the listener: 28 $configListener->setMergedConfig($config); 29 } 30 } At this point, the merged application configuration will no longer contain the key some_key. Note: If a cached config is used by the ModuleManager, the EVENT_MERGE_CONFIG event will not be triggered. However, typically that means that what is cached will be what was originally manipulated by your listener. 29.6 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 ini- tialize the system. • Application configuration – The ModuleManager loops through each module class in the order defined in the system configuration * 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. 29.6. Configuration merging workflow 121
  • 162. Zend Framework 2 Documentation, Release 2.3.1dev – ConfigListener triggers EVENT_MERGE_CONFIG: - ConfigListener merges configuration - Any other event listeners manipulate the configuration – Merged configuration is finally passed to the ServiceManager 122 Chapter 29. Advanced Configuration Tricks
  • 163. CHAPTER 30 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. 30.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 ’home’ => array( 2 ’type’ => ’ZendMvcRouterHttpLiteral’, 3 ’options’ => array( 4 ’route’ => ’/’, 5 ’defaults’ => array( 6 ’controller’ => ’ApplicationControllerIndex’, // <-- change back here 7 ’action’ => ’index’, 8 ), 9 ), 10 ), 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. 30.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 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 123
  • 164. Zend Framework 2 Documentation, Release 2.3.1dev 1 ’service_manager’ => array( 2 ’factories’ => array( 3 ’navigation’ => ’ZendNavigationServiceDefaultNavigationFactory’, // <-- add this 4 ), 5 ), 30.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 return array( 2 ... 3 ’navigation’ => array( 4 ’default’ => array( 5 array( 6 ’label’ => ’Home’, 7 ’route’ => ’home’, 8 ), 9 array( 10 ’label’ => ’Album’, 11 ’route’ => ’album’, 12 ’pages’ => array( 13 array( 14 ’label’ => ’Add’, 15 ’route’ => ’album’, 16 ’action’ => ’add’, 17 ), 18 array( 19 ’label’ => ’Edit’, 20 ’route’ => ’album’, 21 ’action’ => ’edit’, 22 ), 23 array( 24 ’label’ => ’Delete’, 25 ’route’ => ’album’, 26 ’action’ => ’delete’, 27 ), 28 ), 29 ), 30 ), 31 ), 32 ... 33 ); 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. 124 Chapter 30. Using ZendNavigation in your Album Module
  • 165. Zend Framework 2 Documentation, Release 2.3.1dev 30.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 <div class="collapse navbar-collapse"> 3 <ul class="nav navbar-nav"> 4 <?php // <-- Add this !! 5 echo $this->navigation(’navigation’)->menu(); 6 ?> 7 ... 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 <div class="collapse navbar-collapse"> 2 <ul class="nav navbar-nav"> 3 <?php // <-- Update this !! 4 echo $this->navigation(’navigation’) 5 ->menu() 6 ->setMinDepth(0) 7 ->setMaxDepth(0) 8 ->setUlClass(’nav navbar-nav’); 9 ?> 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. 30.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 <div class="container"> 3 <?php echo $this->navigation(’navigation’)->breadcrumbs()->setMinDepth(0); // <-- Add this!! ?> 4 <?php echo $this->content; ?> 5 </div> 6 ... 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 30.4. Adding the Menu View Helper 125
  • 166. Zend Framework 2 Documentation, Release 2.3.1dev 1 <ul class="breadcrumb"> 2 <?php 3 // iterate through the pages 4 foreach ($this->pages as $key => $page): 5 ?> 6 <li> 7 <?php 8 // if this isn’t the last page, add a link and the separator 9 if ($key < count($this->pages) - 1): 10 ?> 11 <a href="<?php echo $page->getHref(); ?>"><?php echo $page->getLabel(); ?></a> 12 <?php 13 // otherwise, just output the name 14 else: 15 ?> 16 <?php echo $page->getLabel(); ?> 17 <?php endif; ?> 18 </li> 19 <?php endforeach; ?> 20 </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 <div class="container"> 3 <?php 4 echo $this->navigation(’navigation’) // <-- Update this!! 5 ->breadcrumbs() 6 ->setMinDepth(0) 7 ->setPartial(array(’partial/breadcrumb.phtml’, ’Album’)); 8 ?> 9 <?php echo $this->content; ?> 10 </div> 11 ... Refreshing the page now gives us a lovely styled set of breadcrumbs on each page. 126 Chapter 30. Using ZendNavigation in your Album Module
  • 167. CHAPTER 31 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: 31.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’), (’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)’), 127
  • 168. Zend Framework 2 Documentation, Release 2.3.1dev (’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 Misérables (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’), (’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’), 128 Chapter 31. Using ZendPaginator in your Album Module
  • 169. Zend Framework 2 Documentation, Release 2.3.1dev (’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 Fältskog’, ’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)’), (’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’), 31.1. Preparation 129
  • 170. Zend Framework 2 Documentation, Release 2.3.1dev (’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. 31.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 <?php 2 namespace AlbumModel; 3 4 use ZendDbResultSetResultSet; 5 use ZendDbTableGatewayTableGateway; 6 use ZendDbSqlSelect; 7 use ZendPaginatorAdapterDbSelect; 8 use ZendPaginatorPaginator; 9 10 class AlbumTable 11 { 12 ... 13 public function fetchAll($paginated=false) 14 { 15 if ($paginated) { 16 // create a new Select object for the table album 17 $select = new Select(’album’); 18 // create a new result set based on the Album entity 19 $resultSetPrototype = new ResultSet(); 20 $resultSetPrototype->setArrayObjectPrototype(new Album()); 21 // create a new pagination adapter object 22 $paginatorAdapter = new DbSelect( 23 // our configured select object 24 $select, 25 // the adapter to run it against 26 $this->tableGateway->getAdapter(), 27 // the result set to hydrate 28 $resultSetPrototype 29 ); 30 $paginator = new Paginator($paginatorAdapter); 31 return $paginator; 130 Chapter 31. Using ZendPaginator in your Album Module
  • 171. Zend Framework 2 Documentation, Release 2.3.1dev 32 } 33 $resultSet = $this->tableGateway->select(); 34 return $resultSet; 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. 31.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 public function indexAction() 3 { 4 // grab the paginator from the AlbumTable 5 $paginator = $this->getAlbumTable()->fetchAll(true); 6 // set the current page to what has been passed in query string, or to 1 if none set 7 $paginator->setCurrentPageNumber((int) $this->params()->fromQuery(’page’, 1)); 8 // set the number of items per page to 10 9 $paginator->setItemCountPerPage(10); 10 11 return new ViewModel(array( 12 ’paginator’ => $paginator 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. 31.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: module/Album/view/album/album/index.phtml 1 <table class="table"> 2 <tr> 3 <th>Title</th> 4 <th>Artist</th> 5 <th>&nbsp;</th> 6 </tr> 7 <?php foreach ($this->paginator as $album) : // <-- change here! ?> 8 <tr> 9 <td><?php echo $this->escapeHtml($album->title);?></td> 10 <td><?php echo $this->escapeHtml($album->artist);?></td> 31.3. Modifying the AlbumController 131
  • 172. Zend Framework 2 Documentation, Release 2.3.1dev 11 <td> 12 <a href="<?php echo $this->url(’album’, 13 array(’action’ => ’edit’, ’id’ => $album->id));?>">Edit</a> 14 <a href="<?php echo $this->url(’album’, 15 array(’action’ => ’delete’, ’id’ => $album->id));?>">Delete</a> 16 </td> 17 </tr> 18 <?php endforeach; ?> 19 </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... 31.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 <?php if ($this->pageCount): ?> 2 <div> 3 <ul class="pagination"> 4 <!-- Previous page link --> 5 <?php if (isset($this->previous)): ?> 6 <li> 7 <a href="<?php echo $this->url($this->route); ?>?page=<?php echo $this->previous 8 << 9 </a> 10 </li> 11 <?php else: ?> 12 <li class="disabled"> 13 <a href="#"> 14 << 15 </a> 16 </li> 17 <?php endif; ?> 18 19 <!-- Numbered page links --> 20 <?php foreach ($this->pagesInRange as $page): ?> 21 <?php if ($page != $this->current): ?> 22 <li> 23 <a href="<?php echo $this->url($this->route);?>?page=<?php echo $page; ?>"> 24 <?php echo $page; ?> 25 </a> 26 </li> 27 <?php else: ?> 28 <li class="active"> 29 <a href="#"><?php echo $page; ?></a> 30 </li> 31 <?php endif; ?> 32 <?php endforeach; ?> 33 132 Chapter 31. Using ZendPaginator in your Album Module
  • 173. Zend Framework 2 Documentation, Release 2.3.1dev 34 <!-- Next page link --> 35 <?php if (isset($this->next)): ?> 36 <li> 37 <a href="<?php echo $this->url($this->route); ?>?page=<?php echo $this->next; ?> 38 >> 39 </a> 40 </li> 41 <?php else: ?> 42 <li class="disabled"> 43 <a href="#"> 44 >> 45 </a> 46 </li> 47 <?php endif; ?> 48 </ul> 49 </div> 50 <?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. 31.5. Creating the Pagination Control Partial 133
  • 174. Zend Framework 2 Documentation, Release 2.3.1dev 134 Chapter 31. Using ZendPaginator in your Album Module
  • 175. CHAPTER 32 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 <?php 3 // add at the end of the file after the table 4 echo $this->paginationControl( 5 // the paginator object 6 $this->paginator, 7 // the scrolling style 8 ’sliding’, 9 // the partial to use to render the control 10 array(’partial/paginator.phtml’, ’Album’), 11 // the route to link to when a user clicks a control link 12 array( 13 ’route’ => ’album’ 14 ) 15 ); 16 ?> 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! 135
  • 176. Zend Framework 2 Documentation, Release 2.3.1dev 136 Chapter 32. Using the PaginationControl View Helper
  • 177. CHAPTER 33 Setting up a database adapter 33.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. 33.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 return array( 4 ’db’ => array( 5 ’driver’ => ’Pdo’, 6 ’dsn’ => ’mysql:dbname=zf2tutorial;host=localhost’, 7 ), 8 ’service_manager’ => array( 9 ’factories’ => array( 10 ’ZendDbAdapterAdapter’ => ’ZendDbAdapterAdapterServiceFactory’, 11 ), 12 ), 13 ); The adapter can then be accessed in any ServiceLocatorAware classes. 1 public function getAdapter() 2 { 3 if (!$this->adapter) { 4 $sm = $this->getServiceLocator(); 5 $this->adapter = $sm->get(’ZendDbAdapterAdapter’); 6 } 7 return $this->adapter; 8 } More information on adapter options can be found in the docs for ZendDbAdapter. 137
  • 178. Zend Framework 2 Documentation, Release 2.3.1dev 33.3 Setting a static adapter In order to utilize this adapter in non-ServiceLocatorAware classes, you can use ZendDbTableGatewayFeatureGlobalAdapterFeature::setStaticAdapter() to set a static adapter: 1 // config/autoload/global.php 2 3 return array( 4 ’db’ => array( 5 ’driver’ => ’Pdo’, 6 ’dsn’ => ’mysql:dbname=zf2tutorial;host=localhost’, 7 ), 8 ’service_manager’ => array( 9 ’factories’ => array( 10 ’ZendDbAdapterAdapter’ => function ($serviceManager) { 11 $adapterFactory = new ZendDbAdapterAdapterServiceFactory(); 12 $adapter = $adapterFactory->createService($serviceManager); 13 14 ZendDbTableGatewayFeatureGlobalAdapterFeature::setStaticAdapter($adapter); 15 16 return $adapter; 17 } 18 ), 19 ), 20 ); The adapter can then later be fetched using ZendDbTableGatewayFeatureGlobalAdapterFeature::getStaticAda for use in e.g. ZendValidatorDbRecordExists: 1 $validator = new ZendValidatorDbRecordExists( 2 array( 3 ’table’ => ’users’, 4 ’field’ => ’emailaddress’, 5 ’adapter’ => ZendDbTableGatewayFeatureGlobalAdapterFeature::getStaticAdapter() 6 ) 7 ); 138 Chapter 33. Setting up a database adapter
  • 179. CHAPTER 34 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. 139
  • 180. Zend Framework 2 Documentation, Release 2.3.1dev 140 Chapter 34. Migration from Zend Framework 1
  • 181. CHAPTER 35 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. 35.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. 141
  • 182. Zend Framework 2 Documentation, Release 2.3.1dev 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. 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 142 Chapter 35. Namespacing Old Classes
  • 183. Zend Framework 2 Documentation, Release 2.3.1dev inheritance), model and form classes might not have the same level of coupling. 35.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 <?php return array ( 2 array ( 3 ’root_directory’ => ’/realpath/to/project/application/models’, 4 ’original_class’ => ’Application_Model_Invoice’, 5 ’original_file’ => ’/realpath/to/project/application/models/Invoice.php’, 6 ’new_namespace’ => ’ApplicationModel’, 7 ’new_class’ => ’Invoice’, 8 ’new_file’ => ’/realpath/to/project/application/models/Application/Model/Invoice.php’, 9 ), 10 ... 11 ); 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: models/DbTable/Invoice.php -> models/Application/Model/DbTable/Invoice.php new file: models/Application/Model/DbTable/Transaction.php renamed: models/Invoice.php -> models/Application/Model/Invoice.php renamed: models/Payment/Paypal/DirectPayment.php -> models/Application/Model/Payment/Paypal/DirectPa renamed: models/PurchaseOrder.php -> models/Application/Model/PurchaseOrder.php renamed: models/PurchaseOrderRepository.php -> models/Application/Model/PurchaseOrderRepository.php new file: models/Application/Model/PurchaseOrderService.php renamed: models/Purchaser.php -> models/Application/Model/Purchaser.php renamed: models/Ticket.php -> models/Application/Model/Ticket.php renamed: models/Transaction.php -> models/Application/Model/Transaction.php renamed: models/TransactionRepository.php -> models/Application/Model/TransactionRepository.php deleted: models/DbTable/Transaction.php deleted: models/PurchaseOrderService.php 35.2. HOWTO Namespace Your Models 143
  • 184. Zend Framework 2 Documentation, Release 2.3.1dev 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 use ApplicationModelPurchaseOrder; 4 use ApplicationModelTransaction; 5 use Zend_Filter_Alnum; 6 7 class Invoice 8 { 9 10 protected $tickets; 11 protected $transaction; 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. 144 Chapter 35. Namespacing Old Classes
  • 185. CHAPTER 36 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. 36.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 define(’ZF2_PATH’, ’/path/to/zf2/library’); 2 require_once ZF2_PATH . ’/Zend/Loader/StandardAutoloader.php’; 3 $loader = new ZendLoaderStandardAutoloader(array( 4 ’autoregister_zf’ => true, 5 )); 6 $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’)). 36.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. 145
  • 186. Zend Framework 2 Documentation, Release 2.3.1dev 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.23", "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. 36.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 SetEnv APPLICATION_ENV development 2 RewriteEngine On 3 RewriteCond %{REQUEST_FILENAME} -s [OR] 4 RewriteCond %{REQUEST_FILENAME} -l [OR] 5 RewriteCond %{REQUEST_FILENAME} -d 6 RewriteRule ^ - [NC,L] 7 RewriteRule ^album(/.*)?$ index_zf2.php [NC,L] 8 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. 146 Chapter 36. Running Zend Framework 2 and Zend Framework 1 in parallel
  • 187. CHAPTER 37 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 authenti- cation adapters and persistent storage backends. 37.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 creden- tials (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 class MyAuthAdapter implements AdapterInterface 4 { 5 /** 6 * Sets username and password for authentication 7 * 8 * @return void 9 */ 147
  • 188. Zend Framework 2 Documentation, Release 2.3.1dev 10 public function __construct($username, $password) 11 { 12 // ... 13 } 14 15 /** 16 * Performs an authentication attempt 17 * 18 * @return ZendAuthenticationResult 19 * @throws ZendAuthenticationAdapterExceptionExceptionInterface 20 * If authentication cannot be performed 21 */ 22 public function authenticate() 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. 37.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 au- thentication 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 op- erations. 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 Result::SUCCESS 4 Result::FAILURE 5 Result::FAILURE_IDENTITY_NOT_FOUND 6 Result::FAILURE_IDENTITY_AMBIGUOUS 7 Result::FAILURE_CREDENTIAL_INVALID 8 Result::FAILURE_UNCATEGORIZED 148 Chapter 37. Introduction to ZendAuthentication
  • 189. Zend Framework 2 Documentation, Release 2.3.1dev The following example illustrates how a developer may branch on the result code: 1 // inside of AuthController / loginAction 2 $result = $this->auth->authenticate($adapter); 3 4 switch ($result->getCode()) { 5 6 case Result::FAILURE_IDENTITY_NOT_FOUND: 7 /** do stuff for nonexistent identity **/ 8 break; 9 10 case Result::FAILURE_CREDENTIAL_INVALID: 11 /** do stuff for invalid credential **/ 12 break; 13 14 case Result::SUCCESS: 15 /** do stuff for successful authentication **/ 16 break; 17 18 default: 19 /** do stuff for other failure **/ 20 break; 21 } 37.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. 37.3.1 Default Persistence in the PHP Session By default, ZendAuthentication provides persistent storage of the identity from a suc- cessful authentication attempt using the PHP session. Upon a successful authentication at- tempt, ZendAuthenticationAuthenticationService::authenticate() stores the identity from the authentication result into persistent storage. Unless specified other- wise, 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. 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 37.3. Identity Persistence 149
  • 190. Zend Framework 2 Documentation, Release 2.3.1dev constructor of ZendSessionContainer. This should occur before authentication is attempted, since ZendAuthenticationAuthenticationService::authenticate() performs the automatic storage of the identity. 1 use ZendAuthenticationAuthenticationService; 2 use ZendAuthenticationStorageSession as SessionStorage; 3 4 $auth = new AuthenticationService(); 5 6 // Use ’someNamespace’ instead of ’Zend_Auth’ 7 $auth->setStorage(new SessionStorage(’someNamespace’)); 8 9 /** 10 * @todo Set up the auth adapter, $authAdapter 11 */ 12 13 // Authenticate, saving the result, and persisting the identity on 14 // success 15 $result = $auth->authenticate($authAdapter); 37.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 $storage = new Chain; 2 $storage->add(new Session); 3 $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 $chain->add(new A, 2); 2 $chain->add(new B, 10); // First use B 37.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- ment ZendAuthenticationStorageStorageInterface and supply an instance of the class to ZendAuthenticationAuthenticationService::setStorage(). 150 Chapter 37. Introduction to ZendAuthentication
  • 191. Zend Framework 2 Documentation, Release 2.3.1dev 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 class MyStorage implements StorageInterface 4 { 5 /** 6 * Returns true if and only if storage is empty 7 * 8 * @throws ZendAuthenticationExceptionExceptionInterface 9 * If it is impossible to 10 * determine whether storage is empty 11 * @return boolean 12 */ 13 public function isEmpty() 14 { 15 /** 16 * @todo implementation 17 */ 18 } 19 20 /** 21 * Returns the contents of storage 22 * 23 * Behavior is undefined when storage is empty. 24 * 25 * @throws ZendAuthenticationExceptionExceptionInterface 26 * If reading contents from storage is impossible 27 * @return mixed 28 */ 29 30 public function read() 31 { 32 /** 33 * @todo implementation 34 */ 35 } 36 37 /** 38 * Writes $contents to storage 39 * 40 * @param mixed $contents 41 * @throws ZendAuthenticationExceptionExceptionInterface 42 * If writing $contents to storage is impossible 43 * @return void 44 */ 45 46 public function write($contents) 47 { 48 /** 49 * @todo implementation 50 */ 51 } 52 53 /** 37.3. Identity Persistence 151
  • 192. Zend Framework 2 Documentation, Release 2.3.1dev 54 * Clears contents from storage 55 * 56 * @throws ZendAuthenticationExceptionExceptionInterface 57 * If clearing contents from storage is impossible 58 * @return void 59 */ 60 61 public function clear() 62 { 63 /** 64 * @todo implementation 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 // Instruct AuthenticationService to use the custom storage class 4 $auth = new AuthenticationService(); 5 6 $auth->setStorage(new MyStorage()); 7 8 /** 9 * @todo Set up the auth adapter, $authAdapter 10 */ 11 12 // Authenticate, saving the result, and persisting the identity on 13 // success 14 $result = $auth->authenticate($authAdapter); 37.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 // instantiate the authentication service 4 $auth = new AuthenticationService(); 5 6 // Set up the authentication adapter 7 $authAdapter = new MyAuthAdapter($username, $password); 8 9 // Attempt authentication, saving the result 10 $result = $auth->authenticate($authAdapter); 11 12 if (!$result->isValid()) { 13 // Authentication failed; print the reasons why 152 Chapter 37. Introduction to ZendAuthentication
  • 193. Zend Framework 2 Documentation, Release 2.3.1dev 14 foreach ($result->getMessages() as $message) { 15 echo "$messagen"; 16 } 17 } else { 18 // Authentication succeeded; the identity ($username) is stored 19 // in the session 20 // $result->getIdentity() === $auth->getIdentity() 21 // $result->getIdentity() === $username 22 } 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 * @todo Set up the auth adapter, $authAdapter 7 */ 8 9 if ($auth->hasIdentity()) { 10 // Identity exists; get it 11 $identity = $auth->getIdentity(); 12 } 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 di- rectly. 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 // Set up the authentication adapter 2 $authAdapter = new MyAuthAdapter($username, $password); 3 4 // Attempt authentication, saving the result 5 $result = $authAdapter->authenticate(); 6 7 if (!$result->isValid()) { 8 // Authentication failed; print the reasons why 9 foreach ($result->getMessages() as $message) { 10 echo "$messagen"; 11 } 12 } else { 13 // Authentication succeeded 14 // $result->getIdentity() === $username 15 } 37.4. Usage 153
  • 194. Zend Framework 2 Documentation, Release 2.3.1dev 154 Chapter 37. Introduction to ZendAuthentication
  • 195. CHAPTER 38 Database Table Authentication 38.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, ob- scured, 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. 38.2 Basic Usage As explained in the introduction, the ZendAuthenticationAdapterDbTable constructor requires an in- stance 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: 1 use ZendDbAdapterAdapter as DbAdapter; 2 3 // Create a SQLite database connection 4 $dbAdapter = new DbAdapter(array( 155
  • 196. Zend Framework 2 Documentation, Release 2.3.1dev 5 ’driver’ => ’Pdo_Sqlite’, 6 ’database’ => ’path/to/sqlite.db’ 7 )); 8 9 // Build a simple table creation query 10 $sqlCreate = ’CREATE TABLE [users] (’ 11 . ’[id] INTEGER NOT NULL PRIMARY KEY, ’ 12 . ’[username] VARCHAR(50) UNIQUE NOT NULL, ’ 13 . ’[password] VARCHAR(32) NULL, ’ 14 . ’[real_name] VARCHAR(150) NULL)’; 15 16 // Create the authentication credentials table 17 $dbAdapter->query($sqlCreate); 18 19 // Build a query to insert a row for which authentication may succeed 20 $sqlInsert = "INSERT INTO users (username, password, real_name) " 21 . "VALUES (’my_username’, ’my_password’, ’My Real Name’)"; 22 23 // Insert the data 24 $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 // Configure the instance with constructor parameters... 4 $authAdapter = new AuthAdapter($dbAdapter, 5 ’users’, 6 ’username’, 7 ’password’ 8 ); 9 10 // ...or configure the instance with setter methods 11 $authAdapter = new AuthAdapter($dbAdapter); 12 13 $authAdapter 14 ->setTableName(’users’) 15 ->setIdentityColumn(’username’) 16 ->setCredentialColumn(’password’) 17 ; 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 // Set the input credential values (e.g., from a login form) 2 $authAdapter 3 ->setIdentity(’my_username’) 4 ->setCredential(’my_password’) 5 ; 6 7 // Perform the authentication query, saving the result In addition to the availability of the getIdentity() method upon the authentication result object, ZendAuthenticationAdapterDbTable also supports retrieving the table row upon authentication suc- cess: 156 Chapter 38. Database Table Authentication
  • 197. Zend Framework 2 Documentation, Release 2.3.1dev 1 // Print the identity 2 echo $result->getIdentity() . "nn"; 3 4 // Print the result row 5 print_r($authAdapter->getResultRowObject()); 6 7 /* Output: 8 my_username 9 10 Array 11 ( 12 [id] => 1 13 [username] => my_username 14 [password] => my_password 15 [real_name] => My Real Name 16 ) 17 */ 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 $columnsToReturn = array( 2 ’id’, ’username’, ’real_name’ 3 ); 4 print_r($authAdapter->getResultRowObject($columnsToReturn)); 5 6 /* Output: 7 8 Array 9 ( 10 [id] => 1 11 [username] => my_username 12 [real_name] => My Real Name 13 ) 14 */ 15 16 $columnsToOmit = array(’password’); 17 print_r($authAdapter->getResultRowObject(null, $columnsToOmit); 18 19 /* Output: 20 21 Array 22 ( 23 [id] => 1 24 [username] => my_username 25 [real_name] => My Real Name 26 ) 27 */ 38.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: 38.3. Advanced Usage: Persisting a DbTable Result Object 157
  • 198. Zend Framework 2 Documentation, Release 2.3.1dev 1 // authenticate with ZendAuthenticationAdapterDbTable 2 $result = $this->_auth->authenticate($adapter); 3 4 if ($result->isValid()) { 5 // store the identity as an object where only the username and 6 // real_name have been returned 7 $storage = $this->_auth->getStorage(); 8 $storage->write($adapter->getResultRowObject(array( 9 ’username’, 10 ’real_name’, 11 ))); 12 13 // store the identity as an object where the password column has 14 // been omitted 15 $storage->write($adapter->getResultRowObject( 16 null, 17 ’password’ 18 )); 19 20 /* ... */ 21 22 } else { 23 24 /* ... */ 25 26 } 38.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 mecha- nisms that can be leveraged for additional checks at authentication time to solve some common user problems. 1 use ZendAuthenticationAdapterDbTable as AuthAdapter; 2 3 // The status field value of an account is not equal to "compromised" 4 $adapter = new AuthAdapter($db, 5 ’users’, 6 ’username’, 7 ’password’, 8 ’MD5(?) AND status != "compromised"’ 9 ); 10 11 // The active field value of an account is equal to "TRUE" 12 $adapter = new AuthAdapter($db, 13 ’users’, 14 ’username’, 15 ’password’, 16 ’MD5(?) AND active = "TRUE"’ 17 ); Another scenario can be the implementation of a salting mechanism. Salting is a term referring to a technique which 158 Chapter 38. Database Table Authentication
  • 199. Zend Framework 2 Documentation, Release 2.3.1dev 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 $sqlAlter = "ALTER TABLE [users] " 2 . "ADD COLUMN [password_salt] " 3 . "AFTER [password]"; Here’s a simple way to generate a salt string for every user at registration: 1 $dynamicSalt = ’’; 2 for ($i = 0; $i < 50; $i++) { 3 $dynamicSalt .= chr(rand(33, 126)); 4 } And now let’s build the adapter: 1 $adapter = new AuthAdapter($db, 2 ’users’, 3 ’username’, 4 ’password’, 5 "MD5(CONCAT(’staticSalt’, ?, password_salt))" 6 ); 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 // Continuing with the example from above 2 $adapter = new AuthAdapter($db, 3 ’users’, 4 ’username’, 5 ’password’, 6 ’MD5(?)’ 7 ); 8 9 // get select object (by reference) 10 $select = $adapter->getDbSelect(); 11 $select->where(’active = "TRUE"’); 12 13 // authenticate, this ensures that users.active = TRUE 14 $adapter->authenticate(); 38.3. Advanced Usage: Persisting a DbTable Result Object 159
  • 200. Zend Framework 2 Documentation, Release 2.3.1dev 160 Chapter 38. Database Table Authentication
  • 201. CHAPTER 39 Digest Authentication 39.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 39.2 Specifics The digest authentication adapter, ZendAuthenticationAdapterDigest, requires several input parame- ters: • 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(). 39.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. 161
  • 202. Zend Framework 2 Documentation, Release 2.3.1dev 1 use ZendAuthenticationAdapterDigest as AuthAdapter; 2 3 $adapter = new AuthAdapter($filename, 4 $realm, 5 $username, 6 $password); 7 8 $result = $adapter->authenticate(); 9 10 $identity = $result->getIdentity(); 11 12 print_r($identity); 13 14 /* 15 Array 16 ( 17 [realm] => Some Realm 18 [username] => someUser 19 ) 20 */ 162 Chapter 39. Digest Authentication
  • 203. CHAPTER 40 HTTP Authentication Adapter 40.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. 40.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. 40.3 Configuration Options The ZendAuthenticationAdapterHttp class requires a configuration array passed to its constructor. There are several configuration options available, and some are required: 163
  • 204. Zend Framework 2 Documentation, Release 2.3.1dev Table 40.1: Configuration Options Option Name Required Description ac- cept_schemes Yes Determines which authentication schemes the adapter will accept from the client. Must be a space-separated list containing ‘basic’ and/or ‘digest’. realm Yes Sets the authentication realm; usernames should be unique within a given realm. di- gest_domains Yes, when accept_schemes contains digest Space-separated list of URIs for which the same authentication information is valid. The URIs need not all point to the same server. nonce_timeoutYes, when accept_schemes contains digest Sets the number of seconds for which the nonce is valid. See notes below. use_opaqueNo Specifies whether to send the opaque value in the header. True by default. algo- rithm No Specified the algorithm. Defaults to MD5, the only supported option (for now). proxy_authNo 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 sup- posed 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 cre- dentials every hour, on the hour. This will be resolved in a future release, once nonce tracking and stale support are implemented. 40.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. 40.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 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: 164 Chapter 40. HTTP Authentication Adapter
  • 205. Zend Framework 2 Documentation, Release 2.3.1dev 1 use ZendAuthenticationAdapterHttpFileResolver; 2 $path = ’files/passwd.txt’; 3 $resolver = new FileResolver($path); or 1 $path = ’files/passwd.txt’; 2 $resolver = new FileResolver(); 3 $resolver->setFile($path); If the given path is empty or not readable, an exception is thrown. 40.5 Basic Usage First, set up an array with the required configuration values: 1 $config = array( 2 ’accept_schemes’ => ’basic digest’, 3 ’realm’ => ’My Web Site’, 4 ’digest_domains’ => ’/members_only /my_account’, 5 ’nonce_timeout’ => 3600, 6 ); 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 $basicResolver = new FileResolver(); 4 $basicResolver->setFile(’files/basicPasswd.txt’); 5 6 $digestResolver = new FileResolver(); 7 $digestResolver->setFile(’files/digestPasswd.txt’); 8 9 $adapter->setBasicResolver($basicResolver); 10 $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 assert($request instanceof ZendHttpRequest); 2 assert($response instanceof ZendHttpResponse); 3 4 $adapter->setRequest($request); 5 $adapter->setResponse($response); 6 7 $result = $adapter->authenticate(); 8 if (!$result->isValid()) { 9 // Bad username/password, or canceled password prompt 10 } 40.5. Basic Usage 165
  • 206. Zend Framework 2 Documentation, Release 2.3.1dev 166 Chapter 40. HTTP Authentication Adapter
  • 207. CHAPTER 41 LDAP Authentication 41.1 Introduction ZendAuthenticationAdapterLdap supports web application authentication with LDAP services. Its fea- tures 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. 41.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 use ZendAuthenticationAuthenticationService; 2 use ZendAuthenticationAdapterLdap as AuthAdapter; 3 use ZendConfigReaderIni as ConfigReader; 4 use ZendConfigConfig; 5 use ZendLogLogger; 6 use ZendLogWriterStream as LogWriter; 7 use ZendLogFilterPriority as LogFilter; 8 9 $username = $this->getRequest()->getPost(’username’); 10 $password = $this->getRequest()->getPost(’password’); 11 12 13 $auth = new AuthenticationService(); 14 15 $configReader = new ConfigReader(); 16 $configData = $configReader->fromFile(’./ldap-config.ini’); 17 $config = new Config($configData, true); 18 19 $log_path = $config->production->ldap->log_path; 20 $options = $config->production->ldap->toArray(); 21 unset($options[’log_path’]); 22 23 $adapter = new AuthAdapter($options, 167
  • 208. Zend Framework 2 Documentation, Release 2.3.1dev 24 $username, 25 $password); 26 27 $result = $auth->authenticate($adapter); 28 29 if ($log_path) { 30 $messages = $result->getMessages(); 31 32 $logger = new Logger; 33 $writer = new LogWriter($log_path); 34 35 $logger->addWriter($writer); 36 37 $filter = new LogFilter(Logger::DEBUG); 38 $writer->addFilter($filter); 39 40 foreach ($messages as $i => $message) { 41 if ($i-- > 1) { // $messages[2] and up are log messages 42 $message = str_replace("n", "n ", $message); 43 $logger->debug("Ldap: $i: $message"); 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 ; Typical options for OpenLDAP 6 ldap.server1.host = s0.foo.net 7 ldap.server1.accountDomainName = foo.net 8 ldap.server1.accountDomainNameShort = FOO 9 ldap.server1.accountCanonicalForm = 3 10 ldap.server1.username = "CN=user1,DC=foo,DC=net" 11 ldap.server1.password = pass1 12 ldap.server1.baseDn = "OU=Sales,DC=foo,DC=net" 13 ldap.server1.bindRequiresDn = true 14 15 ; Typical options for Active Directory 16 ldap.server2.host = dc1.w.net 17 ldap.server2.useStartTls = true 18 ldap.server2.accountDomainName = w.net 19 ldap.server2.accountDomainNameShort = W 20 ldap.server2.accountCanonicalForm = 3 21 ldap.server2.baseDn = "CN=Users,DC=w,DC=net" 168 Chapter 41. LDAP Authentication
  • 209. Zend Framework 2 Documentation, Release 2.3.1dev 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 multi- ple 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 Win- dows, we provide it here for name canonicalization purposes (described in the Username Canonicalization section below). 41.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 Array 2 ( 3 [server2] => Array 4 ( 5 [host] => dc1.w.net 6 [useStartTls] => 1 7 [accountDomainName] => w.net 8 [accountDomainNameShort] => W 9 [accountCanonicalForm] => 3 10 [baseDn] => CN=Users,DC=w,DC=net 11 ) 12 13 [server1] => Array 14 ( 15 [host] => s0.foo.net 16 [accountDomainName] => foo.net 17 [accountDomainNameShort] => FOO 18 [accountCanonicalForm] => 3 19 [username] => CN=user1,DC=foo,DC=net 20 [password] => pass1 21 [baseDn] => OU=Sales,DC=foo,DC=net 22 [bindRequiresDn] => 1 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? 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 re- verse. For example, the user account CN=Bob Carter,CN=Users,DC=w,DC=net is located directly within the 41.3. The API 169
  • 210. Zend Framework 2 Documentation, Release 2.3.1dev 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 op- tions 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). Alter- natively, they may also be set with the setUsername() and setPassword() methods. 41.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(): 170 Chapter 41. LDAP Authentication
  • 211. Zend Framework 2 Documentation, Release 2.3.1dev Table 41.1: Server Options Name Description host The hostname of LDAP server that these options represent. This option is required. port 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- icalForm A value of 2, 3 or 4 indicating the form to which account names should be canonicalized after 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). accountDo- mainName The FQDN domain name for which the target LDAP server is an authority (e.g., example.com). 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 [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. 41.4. Server Options 171
  • 212. Zend Framework 2 Documentation, Release 2.3.1dev 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. 41.5 Collecting Debugging Messages ZendAuthenticationAdapterLdap collects debugging information within its authenticate() method. This information is stored in the ZendAuthenticationResult object as messages. The array re- turned by ZendAuthenticationResult::getMessages() is described as follows Table 41.2: Debugging Messages Messages Array Index Description Index 0 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. Index 1 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. Indexes 2 and higher 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). 41.6 Common Options for Specific Servers 41.6.1 Options for Active Directory For ADS, the following options are noteworthy: 172 Chapter 41. LDAP Authentication
  • 213. Zend Framework 2 Documentation, Release 2.3.1dev Table 41.3: Options for Active Directory Name Additional Notes host As with all servers, this option is required. useStartTls For the sake of security, this should be TRUE if the server has the necessary certificate installed. useSsl Possibly used as an alternative to useStartTls (see above). baseDn 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. accountCanon- icalForm 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.) accountDo- mainName This is required with AD unless accountCanonicalForm 2 is used, which, again, is discouraged. accountDo- main- NameShort 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. 41.6.2 Options for OpenLDAP For OpenLDAP or a generic LDAP server using a typical posixAccount style schema, the following options are note- worthy: 41.6. Common Options for Specific Servers 173
  • 214. Zend Framework 2 Documentation, Release 2.3.1dev Table 41.4: Options for OpenLDAP Name Additional Notes host As with all servers, this option is required. useStartTls For the sake of security, this should be TRUE if the server has the necessary certificate installed. useSsl Possibly used as an alternative to useStartTls (see above). username 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- icalForm Optional, but the default value is 4 (principal style names like [email protected]), which may not be ideal if your users are used to backslash style names (e.g., FOOalice). For backslash style names use value 3. accountDo- mainName Required unless you’re using accountCanonicalForm 2, which is not recommended. accountDo- main- NameShort If AD is not also being used, this value is not required. Otherwise, if accountCanonicalForm 3 is used, this option is required and should be a short name that corresponds adequately to the accountDomainName (e.g., if your accountDomainName is foo.net, a good accountDomainNameShort value might be FOO). 174 Chapter 41. LDAP Authentication
  • 215. CHAPTER 42 Authentication Validator 42.1 Introduction ZendAuthenticationValidatorAuthentication provides the ability to utilize a validator for an In- putFilter 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 42.2 Basic Usage 1 use ZendAuthenticationAuthenticationService; 2 use ZendAuthenticationValidatorAuthentication as AuthenticationValidator; 3 4 $service = new AuthenticationService(); 5 $adapter = new MyAuthenticationAdapter(); 6 $validator = new AuthenticationValidator( 7 ’service’ => $service, 8 ’adapter’ => $adapter, 9 ); 10 11 $validator->setCredential(’myCredentialContext’); 12 $validator->isValid(’myIdentity’, array( 13 ’myCredentialContext’ => ’myCredential’, 14 )); 175
  • 216. Zend Framework 2 Documentation, Release 2.3.1dev 176 Chapter 42. Authentication Validator
  • 217. CHAPTER 43 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. 177
  • 218. Zend Framework 2 Documentation, Release 2.3.1dev 178 Chapter 43. Introduction to ZendBarcode
  • 219. CHAPTER 44 Barcode creation using ZendBarcodeBarcode class 44.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 // Only the text to draw is required 4 $barcodeOptions = array(’text’ => ’ZEND-FRAMEWORK’); 5 6 // No required options 7 $rendererOptions = array(); 8 $renderer = Barcode::factory( 9 ’code39’, ’image’, $barcodeOptions, $rendererOptions 10 ); 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 use ZendConfigConfig; 2 use ZendBarcodeBarcode; 3 179
  • 220. Zend Framework 2 Documentation, Release 2.3.1dev 4 // Using only one ZendConfigConfig object 5 $config = new Config(array( 6 ’barcode’ => ’code39’, 7 ’barcodeParams’ => array(’text’ => ’ZEND-FRAMEWORK’), 8 ’renderer’ => ’image’, 9 ’rendererParams’ => array(’imageType’ => ’gif’), 10 )); 11 12 $renderer = Barcode::factory($config); 44.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 // Only the text to draw is required 4 $barcodeOptions = array(’text’ => ’ZEND-FRAMEWORK’); 5 6 // No required options 7 $rendererOptions = array(); 8 9 // Draw the barcode in a new image, 10 $imageResource = Barcode::factory( 11 ’code39’, ’image’, $barcodeOptions, $rendererOptions 12 )->draw(); Drawing a barcode with ZendBarcodeBarcode::draw() 1 use ZendBarcodeBarcode; 2 3 // Only the text to draw is required 4 $barcodeOptions = array(’text’ => ’ZEND-FRAMEWORK’); 5 6 // No required options 7 $rendererOptions = array(); 8 9 // Draw the barcode in a new image, 10 $imageResource = Barcode::draw( 11 ’code39’, ’image’, $barcodeOptions, $rendererOptions 12 ); 44.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. 180 Chapter 44. Barcode creation using ZendBarcodeBarcode class
  • 221. Zend Framework 2 Documentation, Release 2.3.1dev Rendering a barcode with the renderer object 1 use ZendBarcodeBarcode; 2 3 // Only the text to draw is required 4 $barcodeOptions = array(’text’ => ’ZEND-FRAMEWORK’); 5 6 // No required options 7 $rendererOptions = array(); 8 9 // Draw the barcode in a new image, 10 // send the headers and the image 11 Barcode::factory( 12 ’code39’, ’image’, $barcodeOptions, $rendererOptions 13 )->render(); This will generate this barcode: Rendering a barcode with ZendBarcodeBarcode::render() 1 use ZendBarcodeBarcode; 2 3 // Only the text to draw is required 4 $barcodeOptions = array(’text’ => ’ZEND-FRAMEWORK’); 5 6 // No required options 7 $rendererOptions = array(); 8 9 // Draw the barcode in a new image, 10 // send the headers and the image 11 Barcode::render( 12 ’code39’, ’image’, $barcodeOptions, $rendererOptions 13 ); This will generate the same barcode as the previous example. 44.3. Rendering a barcode 181
  • 222. Zend Framework 2 Documentation, Release 2.3.1dev 182 Chapter 44. Barcode creation using ZendBarcodeBarcode class
  • 223. CHAPTER 45 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 // Case 1: constructor 6 $barcode = new ObjectCode39($options); 7 8 // Case 2: setOptions() 9 $barcode = new ObjectCode39(); 10 $barcode->setOptions($options); 11 12 // Case 3: individual setters 13 $barcode = new ObjectCode39(); 14 $barcode->setText(’ZEND-FRAMEWORK’) 15 ->setBarHeight(40); 45.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: 183
  • 224. Zend Framework 2 Documentation, Release 2.3.1dev Table 45.1: Common Options Option Data Type Default Value Description barcode- Namespace String ZendBarcodeObjectNamespace of the barcode; for example, if you need to extend the embedding objects barHeight Integer 50 Height of the bars barThick- Width Integer 3 Width of the thick bar barThin- Width Integer 1 Width of the thin bar factor Integer 1 Factor by which to multiply bar widths and font sizes (barHeight, barThinWidth, barThickWidth and fontSize) foreColor Integer 0x000000 (black) Color of the bar and the text. Could be provided as an integer or as a HTML value (e.g. “#333333”) background- Color Integer or String 0xFFFFFF (white) Color of the background. Could be provided as an integer or as a HTML value (e.g. “#333333”) orientation Float 0 Orientation of the barcode font String or Integer NULL Font path to a TTF font or a number between 1 and 5 if using image generation with GD (internal fonts) fontSize Float 10 Size of the font (not applicable with numeric fonts) withBorder Boolean FALSE Draw a border around the barcode and the quiet zones withQuiet- Zones Boolean TRUE Leave a quiet zone before and after the barcode drawText Boolean TRUE Set if the text is displayed below the barcode stretchText Boolean FALSE Specify if the text is stretched all along the barcode withCheck- sum Boolean FALSE Indicate whether or not the checksum is automatically added to the barcode withCheck- sumInText Boolean FALSE Indicate whether or not the checksum is displayed in the textual representation text String NULL The text to represent as a barcode 45.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 individ- ual objects by using the setFont() method. 1 use ZendBarcodeBarcode; 2 3 // In your bootstrap: 4 Barcode::setBarcodeFont(’my_font.ttf’); 5 6 // Later in your code: 7 Barcode::render( 8 ’code39’, 9 ’pdf’, 10 array(’text’ => ’ZEND-FRAMEWORK’) 11 ); // will use ’my_font.ttf’ 12 13 // or: 14 Barcode::render( 15 ’code39’, 16 ’image’, 17 array( 184 Chapter 45. ZendBarcodeBarcode Objects
  • 225. Zend Framework 2 Documentation, Release 2.3.1dev 18 ’text’ => ’ZEND-FRAMEWORK’, 19 ’font’ => 3 20 ) 21 ); // will use the 3rd GD internal font 45.2 Common Additional Getters Table 45.2: Common Getters Getter Data Type Description getType() String Return the name of the barcode class without the namespace (e.g. ZendBarcodeObjectCode39 returns simply “code39”) getRawText() String Return the original text provided to the object getTextToDisplay() String Return the text to display, including, if activated, the checksum value getQuietZone() Integer Return the size of the space needed before and after the barcode without any drawing getInstructions() Array Return drawing instructions as an array. getH- eight($recalculate = false) Integer Return the height of the barcode calculated after possible rotation getWidth($recalculate = false) Integer Return the width of the barcode calculated after possible rotation getOffset- Top($recalculate = false) Integer Return the position of the top of the barcode calculated after possible rotation getOff- setLeft($recalculate = false) Integer Return the position of the left of the barcode calculated after possible rotation orphan 45.3 Description of shipped barcodes You will find below detailed information about all barcode types shipped by default with Zend Framework. 45.3.1 ZendBarcodeObjectError This barcode is a special case. It is internally used to automatically render an exception caught by the ZendBarcode component. 45.3.2 ZendBarcodeObjectCode128 • Name: Code 128 • Allowed characters: the complete ASCII-character set 45.2. Common Additional Getters 185
  • 226. Zend Framework 2 Documentation, Release 2.3.1dev • Checksum: optional (modulo 103) • Length: variable There are no particular options for this barcode. 45.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. 45.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. 45.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 45.3: ZendBarcodeObjectCode25interleaved Options Option Data Type Default Value Description withBearerBars Boolean FALSE Draw a thick bar at the top and the bottom of the barcode. Note: If the number of characters is not even, ZendBarcodeObjectCode25interleaved will automati- cally prepend the missing zero to the barcode text. 186 Chapter 45. ZendBarcodeBarcode Objects
  • 227. Zend Framework 2 Documentation, Release 2.3.1dev 45.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. 45.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. 45.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. 45.3. Description of shipped barcodes 187
  • 228. Zend Framework 2 Documentation, Release 2.3.1dev 45.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. 45.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. 45.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) • 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. 188 Chapter 45. ZendBarcodeBarcode Objects
  • 229. Zend Framework 2 Documentation, Release 2.3.1dev 45.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. 45.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) 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. 45.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. 45.3.15 ZendBarcodeObjectPostnet • Name: Postnet (POSTal Numeric Encoding Technique) 45.3. Description of shipped barcodes 189
  • 230. Zend Framework 2 Documentation, Release 2.3.1dev • Allowed characters:‘0123456789’ • Checksum: mandatory (modulo 10) • Length: 6, 7, 10 or 12 characters (including checksum) There are no particular options for this barcode. 45.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. 45.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. 45.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) 190 Chapter 45. ZendBarcodeBarcode Objects
  • 231. Zend Framework 2 Documentation, Release 2.3.1dev 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. 45.3. Description of shipped barcodes 191
  • 232. Zend Framework 2 Documentation, Release 2.3.1dev 192 Chapter 45. ZendBarcodeBarcode Objects
  • 233. CHAPTER 46 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 // Case 1 6 $renderer = new RendererPdf($options); 7 8 // Case 2 9 $renderer = new RendererPdf(); 10 $renderer->setOptions($options); 11 12 // Case 3 13 $renderer = new RendererPdf(); 14 $renderer->setTopOffset(10); 46.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: 193
  • 234. Zend Framework 2 Documentation, Release 2.3.1dev Table 46.1: Common Options Option Data Type Default Value Description render- erNames- pace String ZendBarcodeRendererNamespace of the renderer; for example, if you need to extend the renderers horizon- talPosi- tion String “left” Can be “left”, “center” or “right”. Can be useful with PDF or if the setWidth() method is used with an image renderer. vertical- Position String “top” Can be “top”, “middle” or “bottom”. Can be useful with PDF or if the setHeight() method is used with an image renderer. leftOffset Integer 0 Top position of the barcode inside the renderer. If used, this value will override the “horizontalPosition” option. topOffset Integer 0 Top position of the barcode inside the renderer. If used, this value will override the “verticalPosition” option. automati- cRender- Error Boolean FALSE 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. module- Size Float 1 Size of a rendering module in the support. barcode ZendBarcodeObjectNULL 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”). 46.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 46.2: ZendBarcodeRendererImage Options Option Data Type Default Value Description height Integer 0 Allow you to specify the height of the result image. If “0”, the height will be calculated by the barcode object. width Integer 0 Allow you to specify the width of the result image. If “0”, the width will be calculated by the barcode object. im- ageType String “png” Specify the image format. Can be “png”, “jpeg”, “jpg” or “gif”. 46.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. 194 Chapter 46. ZendBarcode Renderers
  • 235. CHAPTER 47 ZendCacheStorageAdapter 47.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 ex- tend 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. 47.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 // Via factory: 4 $cache = StorageFactory::factory(array( 5 ’adapter’ => array( 6 ’name’ => ’apc’, 7 ’options’ => array(’ttl’ => 3600), 8 ), 195
  • 236. Zend Framework 2 Documentation, Release 2.3.1dev 9 ’plugins’ => array( 10 ’exception_handler’ => array(’throw_exceptions’ => false), 11 ), 12 )); 13 14 // Alternately: 15 $cache = StorageFactory::adapterFactory(’apc’, array(’ttl’ => 3600)); 16 $plugin = StorageFactory::pluginFactory(’exception_handler’, array( 17 ’throw_exceptions’ => false, 18 )); 19 $cache->addPlugin($plugin); 20 21 // Or manually: 22 $cache = new ZendCacheStorageAdapterApc(); 23 $cache->getOptions()->setTtl(3600); 24 25 $plugin = new ZendCacheStoragePluginExceptionHandler(); 26 $plugin->getOptions()->setThrowExceptions(false); 27 $cache->addPlugin($plugin); 47.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 Data Type Default Value Description ttl integer 0 Time to live namespace string “zfcache” The “namespace” in which cache items will live key_pattern null‘‘|‘‘string null Pattern against which to validate cache keys readable boolean true Enable/Disable reading data from cache writable boolean true Enable/Disable writing data to cache 47.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 getItems(array $keys) Load all items given by $keys returning key-value pairs. 196 Chapter 47. ZendCacheStorageAdapter
  • 237. Zend Framework 2 Documentation, Release 2.3.1dev 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 touchItems(array $keys) Reset lifetime of multiple items. Return type boolean 47.4. The StorageInterface 197
  • 238. Zend Framework 2 Documentation, Release 2.3.1dev 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 47.5 The AvailableSpaceCapableInterface The ZendCacheStorageAvailableSpaceCapableInterface implements a method to make it possi- ble getting the current available space of the storage. getAvailableSpace() Get available space in bytes. Return type integer|float 47.6 The TotalSpaceCapableInterface The ZendCacheStorageTotalSpaceCapableInterface implements a method to make it possible get- ting the total space of the storage. getTotalSpace() Get total space in bytes. Return type integer|float 198 Chapter 47. ZendCacheStorageAdapter
  • 239. Zend Framework 2 Documentation, Release 2.3.1dev 47.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 47.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 47.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 47.10 The FlushableInterface The ZendCacheStorageFlushableInterface implements a method to flush the complete storage. flush() Flush the whole storage. Return type boolean 47.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 47.7. The ClearByNamespaceInterface 199
  • 240. Zend Framework 2 Documentation, Release 2.3.1dev 47.12 The OptimizableInterface The ZendCacheStorageOptimizableInterface implements a method to run optimization processes on the storage. optimize() Optimize the storage. Return type boolean 47.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 47.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 200 Chapter 47. ZendCacheStorageAdapter
  • 241. Zend Framework 2 Documentation, Release 2.3.1dev Table 47.1: Capabilities Capability Value supportedDatatypes null, boolean, integer, double, string, array (serialized), object (serialized) supportedMetadata internal_key, atime, ctime, mtime, rtime, size, hits, ttl minTtl 1 maxTtl 0 staticTtl true ttlPrecision 1 useRequestTime <ini value of apc.use_request_time> expiredRead false maxKeyLength 5182 namespaceIsPrefix true namespaceSeparator <Option value of namespace_separator> Table 47.2: Adapter specific options Name Data Type Default Value Description namespace_separator string ”:” A separator for the namespace and prefix 47.15 The Dba Adapter The ZendCacheStorageAdapterDba adapter stores cache items into dbm like databases us- ing the required PHP extension dba. This adapter implements the following interfaces: • ZendCacheStorageStorageInterface • ZendCacheStorageAvailableSpaceCapableInterface • ZendCacheStorageClearByNamespaceInterface • ZendCacheStorageClearByPrefixInterface • ZendCacheStorageFlushableInterface • ZendCacheStorageIterableInterface • ZendCacheStorageOptimizableInterface • ZendCacheStorageTotalSpaceCapableInterface Table 47.3: Capabilities Capability Value supported- Datatypes string, null => string, boolean => string, integer => string, double => string supportedMeta- data <none> minTtl 0 maxKeyLength 0 namespaceIsPre- fix true namespaceSepara- tor <Option value of namespace_separator> 47.15. The Dba Adapter 201
  • 242. Zend Framework 2 Documentation, Release 2.3.1dev Table 47.4: Adapter specific options Name Data Type Default Value Description names- pace_separator string ”:” A separator for the namespace and prefix pathname string “” Pathname to the database file mode string “c” The mode to open the database Please read dba_open for more information handler string “flatfile” 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. 47.16 The Filesystem Adapter The ZendCacheStorageAdapterFilesystem adapter stores cache items into the filesys- tem. This adapter implements the following interfaces: • ZendCacheStorageStorageInterface • ZendCacheStorageAvailableSpaceCapableInterface • ZendCacheStorageClearByNamespaceInterface • ZendCacheStorageClearByPrefixInterface • ZendCacheStorageClearExpiredInterface • ZendCacheStorageFlushableInterface • ZendCacheStorageIterableInterface • ZendCacheStorageOptimizableInterface • ZendCacheStorageTaggableInterface • ZendCacheStorageTotalSpaceCapableInterface 202 Chapter 47. ZendCacheStorageAdapter
  • 243. Zend Framework 2 Documentation, Release 2.3.1dev Table 47.5: Capabilities Capability Value supported- Datatypes string, null => string, boolean => string, integer => string, double => string supportedMeta- data mtime, filespec, atime, ctime minTtl 1 maxTtl 0 staticTtl false ttlPrecision 1 useRequestTime false expiredRead true maxKeyLength 251 namespaceIsPre- fix true namespaceSepara- tor <Option value of namespace_separator> Table 47.6: Adapter specific options Name Data Type Default Value Description names- pace_separator string ”:” A separator for the namespace and prefix cache_dir string “” Directory to store cache files clear_stat_cache boolean true Call clearstatcache() enabled? dir_level integer 1 Defines how much sub-directories should be created dir_permission integer false 0700 Set explicit permission on creating new directories file_locking boolean true Lock files on writing file_permission integer false 0600 Set explicit permission on creating new files key_pattern string /^[a-z0-9_+-]*$/DiValidate key against pattern no_atime boolean true Don’t get ‘fileatime’ as ‘atime’ on metadata no_ctime boolean true Don’t get ‘filectime’ as ‘ctime’ on metadata umask integer false false Use umask to set file and directory permissions 47.17 The Memcached Adapter The ZendCacheStorageAdapterMemcached adapter stores cache items over the mem- cached protocol. It’s using the required PHP extension memcached which is based on Libmemcached. This adapter implements the following interfaces: • ZendCacheStorageStorageInterface • ZendCacheStorageAvailableSpaceCapableInterface • ZendCacheStorageFlushableInterface • ZendCacheStorageTotalSpaceCapableInterface 47.17. The Memcached Adapter 203
  • 244. Zend Framework 2 Documentation, Release 2.3.1dev Table 47.7: Capabilities Capability Value supportedDatatypes null, boolean, integer, double, string, array (serialized), object (serialized) supportedMetadata <none> minTtl 1 maxTtl 0 staticTtl true ttlPrecision 1 useRequestTime false expiredRead false maxKeyLength 255 namespaceIsPrefix true namespaceSeparator <none> Table 47.8: Adapter specific options Name Data Type De- fault Value Description servers array [] List of servers in [] = array(string host, integer port) lib_optionsarray [] 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 47.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 204 Chapter 47. ZendCacheStorageAdapter
  • 245. Zend Framework 2 Documentation, Release 2.3.1dev Table 47.9: Capabilities Capability Value supportedDatatypes string, null, boolean, integer, double, array, object, resource supportedMetadata mtime minTtl 1 maxTtl <Value of PHP_INT_MAX> staticTtl false ttlPrecision 0.05 useRequestTime false expiredRead true maxKeyLength 0 namespaceIsPrefix false Table 47.10: Adapter specific options Name Data Type Default Value Description memory_limit string integer <50% of ini value memory_limit> Limit of how much memory can PHP allocate to allow store items into this adapter • If the used mem- ory of PHP exceeds this limit an OutOf- SpaceException 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. 47.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 47.19. The WinCache Adapter 205
  • 246. Zend Framework 2 Documentation, Release 2.3.1dev Table 47.11: Capabilities Capability Value supportedDatatypes null, boolean, integer, double, string, array (serialized), object (serialized) supportedMetadata internal_key, ttl, hits, size minTtl 1 maxTtl 0 staticTtl true ttlPrecision 1 useRequestTime <ini value of apc.use_request_time> expiredRead false namespaceIsPrefix true namespaceSeparator <Option value of namespace_separator> Table 47.12: Adapter specific options Name Data Type Default Value Description namespace_separator string ”:” A separator for the namespace and prefix 47.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 47.13: Capabilities Capability Value supportedDatatypes boolean, integer, double, string, array (serialized), object (serialized) supportedMetadata internal_key, size, refcount, hits, ctime, atime, hvalue minTtl 1 maxTtl <ini value of xcache.var_maxttl> staticTtl true ttlPrecision 1 useRequestTime true expiredRead false maxKeyLength 5182 namespaceIsPrefix true namespaceSeparator <Option value of namespace_separator> 206 Chapter 47. ZendCacheStorageAdapter
  • 247. Zend Framework 2 Documentation, Release 2.3.1dev Table 47.14: Adapter specific options Name Data Type De- fault Value Description names- pace_separator string”:” A separator for the namespace and prefix ad- min_auth booleanfalse 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. ad- min_user string“” The username of xcache.admin.user ad- min_pass string“” The password of xcache.admin.pass in plain text 47.21 The ZendServerDisk Adapter This ZendCacheStorageAdapterZendServerDisk adapter stores cache items on filesys- tem through the Zend Server Data Caching API. This adapter implements the following interfaces: • ZendCacheStorageStorageInterface • ZendCacheStorageAvailableSpaceCapableInterface • ZendCacheStorageClearByNamespaceInterface • ZendCacheStorageFlushableInterface • ZendCacheStorageTotalSpaceCapableInterface Table 47.15: Capabilities Capability Value supportedDatatypes null, boolean, integer, double, string, array (serialized), object (serialized) supportedMetadata <none> minTtl 1 maxTtl 0 maxKeyLength 0 staticTtl true ttlPrecision 1 useRequestTime false expiredRead false namespaceIsPrefix true namespaceSeparator :: 47.22 The ZendServerShm Adapter The ZendCacheStorageAdapterZendServerShm adapter stores cache items in shared memory through the Zend Server Data Caching API. This adapter implements the following interfaces: 47.21. The ZendServerDisk Adapter 207
  • 248. Zend Framework 2 Documentation, Release 2.3.1dev • ZendCacheStorageStorageInterface • ZendCacheStorageClearByNamespaceInterface • ZendCacheStorageFlushableInterface • ZendCacheStorageTotalSpaceCapableInterface Table 47.16: Capabilities Capability Value supportedDatatypes null, boolean, integer, double, string, array (serialized), object (serialized) supportedMetadata <none> minTtl 1 maxTtl 0 maxKeyLength 0 staticTtl true ttlPrecision 1 useRequestTime false expiredRead false namespaceIsPrefix true namespaceSeparator :: 47.23 Examples Basic usage 1 $cache = ZendCacheStorageFactory::factory(array( 2 ’adapter’ => array( 3 ’name’ => ’filesystem’ 4 ), 5 ’plugins’ => array( 6 // Don’t throw exceptions on cache errors 7 ’exception_handler’ => array( 8 ’throw_exceptions’ => false 9 ), 10 ) 11 )); 12 $key = ’unique-cache-key’; 13 $result = $cache->getItem($key, $success); 14 if (!$success) { 15 $result = doExpensiveStuff(); 16 $cache->setItem($key, $result); 17 } Get multiple rows from db 1 // Instantiate the cache instance using a namespace for the same type of items 2 $cache = ZendCacheStorageFactory::factory(array( 3 ’adapter’ => array( 4 ’name’ => ’filesystem’ 5 // With a namespace we can indicate the same type of items 6 // -> So we can simple use the db id as cache key 7 ’options’ => array( 208 Chapter 47. ZendCacheStorageAdapter
  • 249. Zend Framework 2 Documentation, Release 2.3.1dev 8 ’namespace’ => ’dbtable’ 9 ), 10 ), 11 ’plugins’ => array( 12 // Don’t throw exceptions on cache errors 13 ’exception_handler’ => array( 14 ’throw_exceptions’ => false 15 ), 16 // We store database rows on filesystem so we need to serialize them 17 ’Serializer’ 18 ) 19 )); 20 21 // Load two rows from cache if possible 22 $ids = array(1, 2); 23 $results = $cache->getItems($ids); 24 if (count($results) < count($ids)) { 25 // Load rows from db if loading from cache failed 26 $missingIds = array_diff($ids, array_keys($results)); 27 $missingResults = array(); 28 $query = ’SELECT * FROM dbtable WHERE id IN (’ . implode(’,’, $missingIds) . ’)’; 29 foreach ($pdo->query($query, PDO::FETCH_ASSOC) as $row) { 30 $missingResults[ $row[’id’] ] = $row; 31 } 32 33 // Update cache items of the loaded rows from db 34 $cache->setItems($missingResults); 35 36 // merge results from cache and db 37 $results = array_merge($results, $missingResults); 38 } 47.23. Examples 209
  • 250. Zend Framework 2 Documentation, Release 2.3.1dev 210 Chapter 47. ZendCacheStorageAdapter
  • 251. CHAPTER 48 ZendCacheStorageCapabilities 48.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. 48.2 Available Methods __construct(ZendCacheStorageStorageInterface $storage, stdClass $marker, array $capabilities = ar- ray(), 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 getMinTtl() Get minimum supported time-to-live. (Returning 0 means items never expire) 211
  • 252. Zend Framework 2 Documentation, Release 2.3.1dev 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 setMaxKeyLength(stdClass $marker, int $maxKeyLength) Set maximum key length. Return type ZendCacheStorageCapabilities 212 Chapter 48. ZendCacheStorageCapabilities
  • 253. Zend Framework 2 Documentation, Release 2.3.1dev 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 48.3 Examples Get storage capabilities and do specific stuff in base of it 1 use ZendCacheStorageFactory; 2 3 $cache = StorageFactory::adapterFactory(’filesystem’); 4 $supportedDatatypes = $cache->getCapabilities()->getSupportedDatatypes(); 5 6 // now you can run specific stuff in base of supported feature 7 if ($supportedDatatypes[’object’]) { 8 $cache->set($key, $object); 9 } else { 10 $cache->set($key, serialize($object)); 11 } Listen to change event 1 use ZendCacheStorageFactory; 2 3 $cache = StorageFactory::adapterFactory(’filesystem’, array( 4 ’no_atime’ => false, 5 )); 6 7 // Catching capability changes 8 $cache->getEventManager()->attach(’capability’, function($event) { 9 echo count($event->getParams()) . ’ capabilities changed’; 10 }); 11 12 // change option which changes capabilities 13 $cache->getOptions()->setNoATime(true); 48.3. Examples 213
  • 254. Zend Framework 2 Documentation, Release 2.3.1dev 214 Chapter 48. ZendCacheStorageCapabilities
  • 255. CHAPTER 49 ZendCacheStoragePlugin 49.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 re- sult (with setResult of ZendCacheStoragePostEvent) and catching exceptions (with ZendCacheStorageExceptionEvent). 49.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 // Via factory: 4 $cache = StorageFactory::factory(array( 5 ’adapter’ => ’filesystem’, 6 ’plugins’ => array(’serializer’), 7 )); 8 9 // Alternately: 10 $cache = StorageFactory::adapterFactory(’filesystem’); 11 $plugin = StorageFactory::pluginFactory(’serializer’); 12 $cache->addPlugin($plugin); 13 14 // Or manually: 15 $cache = new ZendCacheStorageAdapterFilesystem(); 16 $plugin = new ZendCacheStoragePluginSerializer(); 17 $cache->addPlugin($plugin); 215
  • 256. Zend Framework 2 Documentation, Release 2.3.1dev 49.3 The ClearExpiredByFactor Plugin The ZendCacheStoragePluginClearExpiredByFactor plugin calls the storage method clearExpired() randomly (by factor) after every call of setItem(), setItems(), addItem() and addItems(). Table 49.1: Plugin specific options Name Data Type Default Value Description clearing_factor integer 0 The automatic clearing factor Note: ** The ClearExpiredInterface is required ** The storage have to implement the ZendCacheStorageClearExpiredInterface to work with this plu- gin. 49.4 The ExceptionHandler Plugin The ZendCacheStoragePluginExceptionHandler 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 49.2: Plugin specific options Name Data Type Default Value Description excep- tion_callback callable null null Callback will be called on an exception and get the exception as argument throw_exceptions boolean true Re-throw catched exceptions 49.5 The IgnoreUserAbort Plugin The ZendCacheStoragePluginIgnoreUserAbort plugin ignores script terminations by users until write operations to cache finished. Table 49.3: Plugin specific options Name Data Type Default Value Description exit_on_abort boolean true Terminate script execution if user abort the script 49.6 The OptimizeByFactor Plugin The ZendCacheStoragePluginOptimizeByFactor plugin calls the storage method optimize() randomly (by factor) after removing items from cache. Table 49.4: Plugin specific options Name Data Type Default Value Description optimizing_factor integer 0 The automatic optimization factor Note: ** The OptimizableInterface is required ** 216 Chapter 49. ZendCacheStoragePlugin
  • 257. Zend Framework 2 Documentation, Release 2.3.1dev The storage have to implement the ZendCacheStorageOptimizableInterface to work with this plugin. 49.7 The Serializer Plugin The ZendCacheStoragePluginSerializer 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 49.5: Plugin specific options Name Data Type Default Value Description serializer null string ZendSerializerAdapterAdapterInterface null The serializer to use • If null use the de- fault serializer • If string instanti- ate the serializer with serializer_options serializer_options array [] Array of serializer options used to instantiate the seri- alizer 49.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 49.9 Examples Basics of writing an own storage plugin 49.7. The Serializer Plugin 217
  • 258. Zend Framework 2 Documentation, Release 2.3.1dev 1 use ZendCacheStorageEvent; 2 use ZendCacheStoragePluginAbstractPlugin; 3 use ZendEventManagerEventManagerInterface; 4 5 class MyPlugin extends AbstractPlugin 6 { 7 8 protected $handles = array(); 9 10 // This method have to attach all events required by this plugin 11 public function attach(EventManagerInterface $events) 12 { 13 $this->handles[] = $events->attach(’getItem.pre’, array($this, ’onGetItemPre’)); 14 $this->handles[] = $events->attach(’getItem.post’, array($this, ’onGetItemPost’)); 15 return $this; 16 } 17 18 // This method have to detach all events required by this plugin 19 public function detach(EventManagerInterface $events) 20 { 21 foreach ($this->handles as $handle) { 22 $events->detach($handle); 23 } 24 $this->handles = array(); 25 return $this; 26 } 27 28 public function onGetItemPre(Event $event) 29 { 30 $params = $event->getParams(); 31 echo sprintf("Method ’getItem’ with key ’%s’ startedn", params[’key’]); 32 } 33 34 public function onGetItemPost(Event $event) 35 { 36 $params = $event->getParams(); 37 echo sprintf("Method ’getItem’ with key ’%s’ finishedn", params[’key’]); 38 } 39 } 40 41 // After defining this basic plugin we can instantiate and add it to an adapter instance 42 $plugin = new MyPlugin(); 43 $cache->addPlugin($plugin); 44 45 // Now on calling getItem our basic plugin should print the expected output 46 $cache->getItem(’cache-key’); 47 // Method ’getItem’ with key ’cache-key’ started 48 // Method ’getItem’ with key ’cache-key’ finished 218 Chapter 49. ZendCacheStoragePlugin
  • 259. CHAPTER 50 ZendCachePattern 50.1 Overview Cache patterns are configurable objects to solve known performance bottlenecks. Each should be used only in the spe- cific 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 ex- tend the abstract class ZendCachePatternAbstractPattern to implement basic logic. Configuration is provided via the ZendCachePatternPatternOptions class, which can simply be instan- tiated 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. 50.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 // Via the factory: 2 $callbackCache = ZendCachePatternFactory::factory(’callback’, array( 3 ’storage’ => ’apc’, 4 )); 1 // OR, the equivalent manual instantiation: 2 $callbackCache = new ZendCachePatternCallbackCache(); 3 $callbackCache->setOptions(new ZendCachePatternPatternOptions(array( 4 ’storage’ => ’apc’, 5 ))); 50.3 Available Methods The following methods are implemented by ZendCachePatternAbstractPattern. Please read docu- mentation of specific patterns to get more information. 219
  • 260. Zend Framework 2 Documentation, Release 2.3.1dev setOptions(ZendCachePatternPatternOptions $options) Set pattern options. Return type ZendCachePatternPatternInterface getOptions() Get all pattern options. Return type ZendCachePatternPatternOptions 220 Chapter 50. ZendCachePattern
  • 261. CHAPTER 51 ZendCachePatternCallbackCache 51.1 Overview The callback cache pattern caches calls of non specific functions and methods given as a callback. 51.2 Quick Start For instantiation you can use the PatternFactory or do it manual: 1 use ZendCachePatternFactory; 2 use ZendCachePatternPatternOptions; 3 4 // Via the factory: 5 $callbackCache = PatternFactory::factory(’callback’, array( 6 ’storage’ => ’apc’, 7 ’cache_output’ => true, 8 )); 9 10 // OR, the equivalent manual instantiation: 11 $callbackCache = new ZendCachePatternCallbackCache(); 12 $callbackCache->setOptions(new PatternOptions(array( 13 ’storage’ => ’apc’, 14 ’cache_output’ => true, 15 ))); 51.3 Configuration Options Option Data Type Default Value Description storage string array ZendCacheStorageStorageInterface <none> The storage to write/read cached data cache_outputboolean true Cache output of callback 221
  • 262. Zend Framework 2 Documentation, Release 2.3.1dev 51.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 51.5 Examples Instantiating the callback cache pattern 1 use ZendCachePatternFactory; 2 3 $callbackCache = PatternFactory::factory(’callback’, array( 4 ’storage’ => ’apc’ 5 )); 222 Chapter 51. ZendCachePatternCallbackCache
  • 263. CHAPTER 52 ZendCachePatternClassCache 52.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. 52.2 Quick Start Instantiating the class cache pattern 1 use ZendCachePatternFactory; 2 3 $classCache = PatternFactory::factory(’class’, array( 4 ’class’ => ’MyClass’, 5 ’storage’ => ’apc’ 6 )); 52.3 Configuration Options Option Data Type Default Value Description storage string array ZendCacheStorageStorageInterface <none> The storage to write/read cached data class string <none> The class name cache_output boolean true Cache output of callback cache_by_default boolean true Cache method calls by default class_cache_methodsarray [] List of methods to cache (If cache_by_default is disabled) class_non_cache_methodsarray [] List of methods to no-cache (If cache_by_default is enabled) 52.4 Available Methods call(string $method, array $args = array()) 223
  • 264. Zend Framework 2 Documentation, Release 2.3.1dev 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 52.5 Examples Caching of import feeds 1 $cachedFeedReader = ZendCachePatternFactory::factory(’class’, array( 2 ’class’ => ’ZendFeedReaderReader’, 3 ’storage’ => ’apc’, 4 5 // The feed reader doesn’t output anything 6 // so the output don’t need to be caught and cached 7 ’cache_output’ => false, 224 Chapter 52. ZendCachePatternClassCache
  • 265. Zend Framework 2 Documentation, Release 2.3.1dev 8 )); 9 10 $feed = $cachedFeedReader->call("import", array(’http://www.planet-php.net/rdf/’)); 11 // OR 12 $feed = $cachedFeedReader->import(’http://www.planet-php.net/rdf/’); 52.5. Examples 225
  • 266. Zend Framework 2 Documentation, Release 2.3.1dev 226 Chapter 52. ZendCachePatternClassCache
  • 267. CHAPTER 53 ZendCachePatternObjectCache 53.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. 53.2 Quick Start Instantiating the object cache pattern 1 use ZendCachePatternFactory; 2 3 $object = new stdClass(); 4 $objectCache = PatternFactory::factory(’object’, array( 5 ’object’ => $object, 6 ’storage’ => ’apc’ 7 )); 53.3 Configuration Options Option Data Type Default Value Description storage string array ZendCacheStorageStorageInterface <none> The storage to write/read cached data object object <none> The object to cache methods calls of object_key null string <Class name of object> A hopefully unique key of the object cache_output boolean true Cache output of callback cache_by_default boolean true Cache method calls by default ob- ject_cache_methods array [] List of methods to cache (If cache_by_default is disabled) ob- ject_non_cache_methods array [] List of methods to no-cache (If cache_by_default is enabled) ob- ject_cache_magic_properties boolean false Cache calls of magic object properties 227
  • 268. Zend Framework 2 Documentation, Release 2.3.1dev 53.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 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 53.5 Examples Caching a filter 228 Chapter 53. ZendCachePatternObjectCache
  • 269. Zend Framework 2 Documentation, Release 2.3.1dev 1 $filter = new ZendFilterRealPath(); 2 $cachedFilter = ZendCachePatternFactory::factory(’object’, array( 3 ’object’ => $filter, 4 ’object_key’ => ’RealpathFilter’, 5 ’storage’ => ’apc’, 6 7 // The realpath filter doesn’t output anything 8 // so the output don’t need to be caught and cached 9 ’cache_output’ => false, 10 )); 11 12 $path = $cachedFilter->call("filter", array(’/www/var/path/../../mypath’)); 13 // OR 14 $path = $cachedFilter->filter(’/www/var/path/../../mypath’); 53.5. Examples 229
  • 270. Zend Framework 2 Documentation, Release 2.3.1dev 230 Chapter 53. ZendCachePatternObjectCache
  • 271. CHAPTER 54 ZendCachePatternOutputCache 54.1 Overview The OutputCache pattern caches output between calls to start() and end(). 54.2 Quick Start Instantiating the output cache pattern 1 use ZendCachePatternFactory; 2 3 $outputCache = PatternFactory::factory(’output’, array( 4 ’storage’ => ’apc’ 5 )); 54.3 Configuration Options Op- tion Data Type Default Value Description stor- age string array ZendCacheStorageStorageInterface <none> The storage to write/read cached data 54.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. Return type boolean setOptions(ZendCachePatternPatternOptions $options) Set pattern options. 231
  • 272. Zend Framework 2 Documentation, Release 2.3.1dev Return type ZendCachePatternOutputCache getOptions() Get all pattern options. Return type ZendCachePatternPatternOptions 54.5 Examples Caching simple view scripts 1 $outputCache = ZendCachePatternFactory::factory(’output’, array( 2 ’storage’ => ’apc’, 3 )); 4 5 $outputCache->start(’mySimpleViewScript’); 6 include ’/path/to/view/script.phtml’; 7 $outputCache->end(); 232 Chapter 54. ZendCachePatternOutputCache
  • 273. CHAPTER 55 ZendCachePatternCaptureCache 55.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. 55.2 Quick Start Simplest usage as Apache-404 handler 1 # .htdocs 2 ErrorDocument 404 /index.php 1 // index.php 2 use ZendCachePatternFactory; 3 $capture = ZendCachePatternFactory::factory(’capture’, array( 4 ’public_dir’ => __DIR__, 5 )); 6 7 // Start capturing all output excl. headers and write to public directory 8 $capture->start(); 9 10 // Don’t forget to change HTTP response code 11 header(’Status: 200’, true, 200); 12 13 // do stuff to dynamically generate output 233
  • 274. Zend Framework 2 Documentation, Release 2.3.1dev 55.3 Configuration Options Option Data Type Default Value Description public_dir string <none> Location of public directory to write output to index_filename string “index.html” The name of the first file if only a directory was requested file_locking boolean true Locking output files on writing file_permission integer boolean 0600 (false on win) Set permissions of generated output files dir_permission integer boolean 0700 (false on win) Set permissions of generated output directories umask integer boolean false Using umask on generating output files / directories 55.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 234 Chapter 55. ZendCachePatternCaptureCache
  • 275. Zend Framework 2 Documentation, Release 2.3.1dev 55.5 Examples Scaling images in base of request 1 # .htdocs 2 ErrorDocument 404 /index.php 1 // index.php 2 $captureCache = ZendCachePatternFactory::factory(’capture’, array( 3 ’public_dir’ => __DIR__, 4 )); 5 6 // TODO 55.5. Examples 235
  • 276. Zend Framework 2 Documentation, Release 2.3.1dev 236 Chapter 55. ZendCachePatternCaptureCache
  • 277. CHAPTER 56 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 multi- ple 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. 237
  • 278. Zend Framework 2 Documentation, Release 2.3.1dev 238 Chapter 56. Introduction to ZendCaptcha
  • 279. CHAPTER 57 Captcha Operation All CAPTCHA adapters implement ZendCaptchaAdapterInterface, which looks like the following: 1 namespace ZendCaptcha; 2 3 use ZendValidatorValidatorInterface; 4 5 interface AdapterInterface extends ValidatorInterface 6 { 7 public function generate(); 8 9 public function setName($name); 10 11 public function getName(); 12 13 // Get helper name used for rendering this captcha type 14 public function getHelperName(); 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 // Originating request: 2 $captcha = new ZendCaptchaFiglet(array( 3 ’name’ => ’foo’, 4 ’wordLen’ => 6, 5 ’timeout’ => 300, 6 )); 7 8 $id = $captcha->generate(); 9 10 //this will output a Figlet string 11 echo $captcha->getFiglet()->render($captcha->getWord()); 12 13 14 // On a subsequent request: 15 // Assume a captcha setup as before, with corresponding form fields, the value of $_POST[’foo’] 16 // would be key/value array: id => captcha ID, input => captcha value 17 if ($captcha->isValid($_POST[’foo’], $_POST)) { 18 // Validated! 239
  • 280. Zend Framework 2 Documentation, Release 2.3.1dev 19 } 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. 240 Chapter 57. Captcha Operation
  • 281. CHAPTER 58 CAPTCHA Adapters The following adapters are shipped with Zend Framework by default. 58.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 interface, ZendCaptchaAbstractWord exposes the following methods: • 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 consid- ered 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. Note: ZendCaptchaAbstractWord is an abstract class and may not be instantiated directly. 241
  • 282. Zend Framework 2 Documentation, Release 2.3.1dev 58.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. 58.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 docu- mentation for details on what configuration options are available. 58.4 ZendCaptchaImage The ZendCaptchaImage adapter takes the generated word and renders it as an image, performing various skew- ing 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 im- ages. 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. • 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 242 Chapter 58. CAPTCHA Adapters
  • 283. Zend Framework 2 Documentation, Release 2.3.1dev 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. 58.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. 58.5. ZendCaptchaReCaptcha 243
  • 284. Zend Framework 2 Documentation, Release 2.3.1dev 244 Chapter 58. CAPTCHA Adapters
  • 285. CHAPTER 59 Introduction ZendCodeGenerator provides facilities to generate arbitrary code using an object-oriented interface, both to create new code as well as to update existing code. While the current implementation is limited to generating PHP code, you can easily extend the base class in order to provide code generation for other tasks: JavaScript, configuration files, apache vhosts, etc. 59.1 Theory of Operation In the most typical use case, you will simply instantiate a code generator class and either pass it the appropriate configuration or configure it after instantiation. To generate the code, you will simply echo the object or call its generate() method. 1 // Passing configuration to the constructor: 2 $file = new ZendCodeGeneratorFileGenerator(array( 3 ’classes’ => array( 4 new ZendCodeGeneratorClassGenerator(array( 5 ’name’ => ’World’, 6 ’methods’ => array( 7 new ZendCodeGeneratorMethodGenerator(array( 8 ’name’ => ’hello’, 9 ’body’ => ’echo ’Hello world!’;’, 10 )), 11 ), 12 )), 13 ) 14 )); 15 16 // Configuring after instantiation 17 $method = new ZendCodeGeneratorMethodGenerator(); 18 $method->setName(’hello’) 19 ->setBody(’echo ’Hello world!’;’); 20 21 $class = new ZendCodeGeneratorClassGenerator(); 22 $class->setName(’World’) 23 ->setMethod($method); 24 25 $file = new ZendCodeGeneratorFileGenerator(); 26 $file->setClass($class); 27 28 // Render the generated file 29 echo $file; 245
  • 286. Zend Framework 2 Documentation, Release 2.3.1dev 30 31 // or write it to a file: 32 file_put_contents(’World.php’, $file->generate()); Both of the above samples will render the same result: 1 <?php 2 3 class World 4 { 5 6 public function hello() 7 { 8 echo ’Hello world!’; 9 } 10 11 } Another common use case is to update existing code – for instance, to add a method to a class. In such a case, you must first inspect the existing code using reflection, and then add your new method. ZendCodeGenerator makes this trivially simple, by leveraging ZendCodeReflection. As an example, let’s say we’ve saved the above to the file World.php, and have already included it. We could then do the following: 1 $class = ZendCodeGeneratorClassGenerator::fromReflection( 2 new ZendCodeReflectionClassReflection(’World’) 3 ); 4 5 $method = new ZendCodeGeneratorMethodGenerator(); 6 $method->setName(’mrMcFeeley’) 7 ->setBody(’echo ’Hello, Mr. McFeeley!’;’); 8 $class->setMethod($method); 9 10 $file = new ZendCodeGeneratorFileGenerator(); 11 $file->setClass($class); 12 13 // Render the generated file 14 echo $file; 15 16 // Or, better yet, write it back to the original file: 17 file_put_contents(’World.php’, $file->generate()); The resulting class file will now look like this: 1 <?php 2 3 class World 4 { 5 6 public function hello() 7 { 8 echo ’Hello world!’; 9 } 10 11 public function mrMcFeeley() 12 { 13 echo ’Hellow Mr. McFeeley!’; 14 } 246 Chapter 59. Introduction
  • 287. Zend Framework 2 Documentation, Release 2.3.1dev 15 16 } 59.1. Theory of Operation 247
  • 288. Zend Framework 2 Documentation, Release 2.3.1dev 248 Chapter 59. Introduction
  • 289. CHAPTER 60 ZendCodeGenerator Reference 60.1 Abstract Classes and Interfaces 60.1.1 ZendCodeGeneratorGeneratorInterface The base interface from which all CodeGenerator classes implement provides the minimal functionality necessary. It’s API is as follows: 1 interface ZendCodeGeneratorGeneratorInterface 2 { 3 public function generate(); 4 } 60.1.2 ZendCodeGeneratorAbstractGenerator ZendCodeGeneratorAbstractGenerator implements ZendCodeGeneratorGeneratorInterface, and adds some properties for tracking whether content has changed as well as the amount of indentation that should appear before generated content. Its API is as follows: 1 abstract class ZendCodeGeneratorAbstractGenerator 2 implements ZendCodeGeneratorGeneratorInterface 3 { 4 public function __construct(Array|Traversable $options = array()) 5 public function setOptions(Array $options) 6 public function setSourceContent($sourceContent) 7 public function getSourceContent() 8 public function setSourceDirty($isSourceDirty = true) 9 public function isSourceDirty() 10 public function setIndentation($indentation) 11 public function getIndentation() 12 } The constructor passes the $options parameter to setOptions(). Like most classes in Zend Framework, setOptions() compares an option key to existing setters in the class, and passes the value on to that method if found. setSourceContent() and getSourceContent() are intended to either set the default content for the code being generated, or to replace said content once all generation tasks are complete. 249
  • 290. Zend Framework 2 Documentation, Release 2.3.1dev 60.1.3 ZendCodeGeneratorAbstractMemberGenerator ZendCodeGeneratorAbstractMemberGenerator is a base class for generating class members – prop- erties and methods – and provides accessors and mutators for establishing visibility; whether or not the member is abstract, static, or final; and the name of the member. Its API is as follows: 1 abstract class ZendCodeGeneratorAbstractMemberGenerator 2 extends ZendCodeGeneratorAbstractGenerator 3 { 4 public function setAbstract($isAbstract) 5 public function isAbstract() 6 public function setStatic($isStatic) 7 public function isStatic() 8 public function setVisibility($visibility) 9 public function getVisibility() 10 public function setName($name) 11 public function getName() 12 } 60.2 Concrete CodeGenerator Classes 60.2.1 ZendCodeGeneratorBodyGenerator ZendCodeGeneratorBodyGenerator is intended for generating arbitrary procedural code to include within a file. As such, you simply set content for the object, and it will return that content when you invoke generate(). The API of the class is as follows: 1 class ZendCodeGeneratorBodyGenerator extends ZendCodeGeneratorAbstractGenerator 2 { 3 public function setContent($content) 4 public function getContent() 5 public function generate() 6 } 60.2.2 ZendCodeGeneratorClassGenerator ZendCodeGeneratorClassGenerator is intended for generating PHP classes. The basic functionality just generates the PHP class itself, as well as optionally the related PHP DocBlock. Classes may implement or inherit from other classes, and may be marked as abstract. Utilizing other code generator classes, you can also attach class constants, properties, and methods. The API is as follows: 1 class ZendCodeGeneratorClassGenerator extends ZendCodeGeneratorAbstractGenerator 2 { 3 public static function fromReflection( 4 ZendCodeReflectionClassReflection $reflectionClass 5 ) 6 public function setDocblock(ZendCodeGeneratorDocBlockGenerator $docblock) 7 public function getDocblock() 8 public function setName($name) 9 public function getName() 10 public function setAbstract($isAbstract) 250 Chapter 60. ZendCodeGenerator Reference
  • 291. Zend Framework 2 Documentation, Release 2.3.1dev 11 public function isAbstract() 12 public function setExtendedClass($extendedClass) 13 public function getExtendedClass() 14 public function setImplementedInterfaces(Array $implementedInterfaces) 15 public function getImplementedInterfaces() 16 public function setProperties(Array $properties) 17 public function setProperty($property) 18 public function getProperties() 19 public function getProperty($propertyName) 20 public function setMethods(Array $methods) 21 public function setMethod($method) 22 public function getMethods() 23 public function getMethod($methodName) 24 public function hasMethod($methodName) 25 public function isSourceDirty() 26 public function generate() 27 } The setProperty() method accepts an array of information that may be used to gener- ate a ZendCodeGeneratorPropertyGenerator instance – or simply an instance of ZendCodeGeneratorPropertyGenerator. Likewise, setMethod() accepts either an array of information for generating a ZendCodeGeneratorMethodGenerator instance or a concrete instance of that class. Note that setDocBlock() expects an instance of ZendCodeGeneratorDocBlockGenerator. 60.2.3 ZendCodeGeneratorDocBlockGenerator ZendCodeGeneratorDocBlockGenerator can be used to generate arbitrary PHP docblocks, including all the standard docblock features: short and long descriptions and annotation tags. Annotation tags may be set using the setTag() and setTags() methods; these each take either an array describing the tag that may be passed to the ZendCodeGeneratorDocBlockTag constructor, or an instance of that class. The API is as follows: 1 class ZendCodeGeneratorDocBlockGenerator extends ZendCodeGeneratorAbstractGenerator 2 { 3 public static function fromReflection( 4 ZendCodeReflectionDocblockReflection $reflectionDocblock 5 ) 6 public function setShortDescription($shortDescription) 7 public function getShortDescription() 8 public function setLongDescription($longDescription) 9 public function getLongDescription() 10 public function setTags(Array $tags) 11 public function setTag($tag) 12 public function getTags() 13 public function generate() 14 } 60.2.4 ZendCodeGeneratorDocBlockTag ZendCodeGeneratorDocBlockTag is intended for creating arbitrary annotation tags for inclusion in PHP docblocks. Tags are expected to contain a name (the portion immediately following the ‘@’ symbol) and a description 60.2. Concrete CodeGenerator Classes 251
  • 292. Zend Framework 2 Documentation, Release 2.3.1dev (everything following the tag name). The class API is as follows: 1 class ZendCodeGeneratorDocBlockTag 2 extends ZendCodeGeneratorAbstractGenerator 3 { 4 public static function fromReflection( 5 ZendCodeReflectionDocBlockTagTagInterface $reflectionTag 6 ) 7 public function setName($name) 8 public function getName() 9 public function setDescription($description) 10 public function getDescription() 11 public function generate() 12 } 60.2.5 ZendCodeGeneratorDocBlockTagParamTag ZendCodeGeneratorDocBlockTagParamTag is a specialized version of ZendCodeGeneratorDocBlockTag, and represents a method parameter. The tag name is therefor known (“param”), but due to the format of this annotation tag, additional information is required in order to generate it: the parameter name and data type it represents. The class API is as follows: 1 class ZendCodeGeneratorDocBlockTagParamTag 2 extends ZendCodeGeneratorDocBlockTag 3 { 4 public static function fromReflection( 5 ZendCodeReflectionDocBlockTagTagInterface $reflectionTagParam 6 ) 7 public function setDatatype($datatype) 8 public function getDatatype() 9 public function setParamName($paramName) 10 public function getParamName() 11 public function generate() 12 } 60.2.6 ZendCodeGeneratorDocBlockTagReturnTag Like the param docblock tag variant, ZendCodeGeneratorDocBlockTagReturnTag is an annotation tag variant for representing a method return value. In this case, the annotation tag name is known (“return”), but requires a return type. The class API is as follows: 1 class ZendCodeGeneratorDocBlockTagParamTag 2 extends ZendCodeGeneratorDocBlockTag 3 { 4 public static function fromReflection( 5 ZendCodeReflectionDocBlockTagTagInterface $reflectionTagReturn 6 ) 7 public function setDatatype($datatype) 8 public function getDatatype() 9 public function generate() 10 } 252 Chapter 60. ZendCodeGenerator Reference
  • 293. Zend Framework 2 Documentation, Release 2.3.1dev 60.2.7 ZendCodeGeneratorFileGenerator ZendCodeGeneratorFileGenerator is used to generate the full contents of a file that will contain PHP code. The file may contain classes or arbitrary PHP code, as well as a file-level docblock if desired. When adding classes to the file, you will need to pass either an array of information to pass to the ZendCodeGeneratorClassGenerator constructor, or an instance of that class. Similarly, with docblocks, you will need to pass information for the ZendCodeGeneratorDocBlockGenerator constructor to con- sume or an instance of the class. The API of the class is as follows: 1 class ZendCodeGeneratorFileGenerator extends ZendCodeGeneratorAbstractGenerator 2 { 3 public static function fromReflectedFilePath( 4 $filePath, 5 $usePreviousCodeGeneratorIfItExists = true, 6 $includeIfNotAlreadyIncluded = true) 7 public static function fromReflection(ZendCodeReflectionFileReflection $reflectionFile) 8 public function setDocblock(ZendCodeGeneratorDocBlockGenerator $docblock) 9 public function getDocblock() 10 public function setRequiredFiles($requiredFiles) 11 public function getRequiredFiles() 12 public function setClasses(Array $classes) 13 public function getClass($name = null) 14 public function setClass($class) 15 public function setFilename($filename) 16 public function getFilename() 17 public function getClasses() 18 public function setBody($body) 19 public function getBody() 20 public function isSourceDirty() 21 public function generate() 22 } 60.2.8 ZendCodeGeneratorMemberContainerGenerator ZendCodeGeneratorMemberContainerGenerator is used internally by ZendCodeGeneratorClassGenerator to keep track of class members – properties and methods alike. These are indexed by name, using the concrete instances of the members as values. The API of the class is as follows: 1 class ZendCodeGeneratorMemberContainerGenerator extends ArrayObject 2 { 3 public function __construct($type = self::TYPE_PROPERTY) 4 } 60.2.9 ZendCodeGeneratorMethodGenerator ZendCodeGeneratorMethodGenerator describes a class method, and can generate both the code and the docblock for the method. The visibility and status as static, abstract, or final may be indicated, per its parent class, ZendCodeGeneratorAbstractMemberGenerator. Finally, the parameters and return value for the method may be specified. 60.2. Concrete CodeGenerator Classes 253
  • 294. Zend Framework 2 Documentation, Release 2.3.1dev Parameters may be set using setParameter() or setParameters(). In each case, a parameter should either be an array of information to pass to the ZendCodeGeneratorParameterGenerator constructor or an instance of that class. The API of the class is as follows: 1 class ZendCodeGeneratorMethodGenerator 2 extends ZendCodeGeneratorAbstractMemberGenerator 3 { 4 public static function fromReflection( 5 ZendCodeReflectionMethodReflection $reflectionMethod 6 ) 7 public function setDocblock(ZendCodeGeneratorDocBlockGenerator $docblock) 8 public function getDocblock() 9 public function setFinal($isFinal) 10 public function setParameters(Array $parameters) 11 public function setParameter($parameter) 12 public function getParameters() 13 public function setBody($body) 14 public function getBody() 15 public function generate() 16 } 60.2.10 ZendCodeGeneratorParameterGenerator ZendCodeGeneratorParameterGenerator may be used to specify method parameters. Each parameter may have a position (if unspecified, the order in which they are registered with the method will be used), a default value, and a data type; a parameter name is required. The API of the class is as follows: 1 class ZendCodeGeneratorParameterGenerator extends ZendCodeGeneratorAbstractGenerator 2 { 3 public static function fromReflection( 4 ZendCodeReflectionParameterReflection $reflectionParameter 5 ) 6 public function setType($type) 7 public function getType() 8 public function setName($name) 9 public function getName() 10 public function setDefaultValue($defaultValue) 11 public function getDefaultValue() 12 public function setPosition($position) 13 public function getPosition() 14 public function getPassedByReference() 15 public function setPassedByReference($passedByReference) 16 public function generate() 17 } There are several problems that might occur when trying to set NULL, booleans or arrays as default values. For this the value holder object ZendCodeGeneratorParameterDefaultValueGenerator can be used, for example: 1 $parameter = new ZendCodeGeneratorParameterGenerator(); 2 $parameter->setDefaultValue( 3 new ZendCodeGeneratorValueGenerator("null") 4 ); 5 $parameter->setDefaultValue( 254 Chapter 60. ZendCodeGenerator Reference
  • 295. Zend Framework 2 Documentation, Release 2.3.1dev 6 new ZendCodeGeneratorValueGenerator("array(’foo’, ’bar’)") 7 ); Internally setDefaultValue() also converts the values which can’t be expressed in PHP into the value holder. 60.2.11 ZendCodeGeneratorPropertyGenerator ZendCodeGeneratorPropertyGenerator describes a class property, which may be either a constant or a variable. In each case, the property may have an optional default value associated with it. Additionally, the visibility of variable properties may be set, per the parent class, ZendCodeGeneratorAbstractMemberGenerator. The API of the class is as follows: 1 class ZendCodeGeneratorPropertyGenerator 2 extends ZendCodeGeneratorAbstractMemberGenerator 3 { 4 public static function fromReflection( 5 ZendCodeReflectionPropertyReflection $reflectionProperty 6 ) 7 public function setConst($const) 8 public function isConst() 9 public function setDefaultValue($defaultValue) 10 public function getDefaultValue() 11 public function generate() 12 } 60.2. Concrete CodeGenerator Classes 255
  • 296. Zend Framework 2 Documentation, Release 2.3.1dev 256 Chapter 60. ZendCodeGenerator Reference
  • 297. CHAPTER 61 ZendCodeGenerator Examples Generating PHP classes The following example generates an empty class with a class-level DocBlock. 1 use ZendCodeGeneratorClassGenerator; 2 use ZendCodeGeneratorDocBlockGenerator; 3 4 $foo = new ClassGenerator(); 5 $docblock = DocBlockGenerator::fromArray(array( 6 ’shortDescription’ => ’Sample generated class’, 7 ’longDescription’ => ’This is a class generated with ZendCodeGenerator.’, 8 ’tags’ => array( 9 array( 10 ’name’ => ’version’, 11 ’description’ => ’$Rev:$’, 12 ), 13 array( 14 ’name’ => ’license’, 15 ’description’ => ’New BSD’, 16 ), 17 ), 18 )); 19 $foo->setName(’Foo’) 20 ->setDocblock($docblock); 21 echo $foo->generate(); The above code will result in the following: 1 /** 2 * Sample generated class 3 * 4 * This is a class generated with ZendCodeGenerator. 5 * 6 * @version $Rev:$ 7 * @license New BSD 8 * 9 */ 10 class Foo 11 { 12 13 } 257
  • 298. Zend Framework 2 Documentation, Release 2.3.1dev Generating PHP classes with class properties Building on the previous example, we now add properties to our generated class. 1 use ZendCodeGeneratorClassGenerator; 2 use ZendCodeGeneratorDocBlockGenerator; 3 use ZendCodeGeneratorPropertyGenerator; 4 5 $foo = new ClassGenerator(); 6 $docblock = DocBlockGenerator::fromArray(array( 7 ’shortDescription’ => ’Sample generated class’, 8 ’longDescription’ => ’This is a class generated with ZendCodeGenerator.’, 9 ’tags’ => array( 10 array( 11 ’name’ => ’version’, 12 ’description’ => ’$Rev:$’, 13 ), 14 array( 15 ’name’ => ’license’, 16 ’description’ => ’New BSD’, 17 ), 18 ), 19 )); 20 $foo->setName(’Foo’) 21 ->setDocblock($docblock) 22 ->setProperties(array( 23 array(’_bar’, ’baz’, PropertyGenerator::FLAG_PROTECTED), 24 array(’baz’, ’bat’, PropertyGenerator::FLAG_PUBLIC), 25 array(’bat’, ’foobarbazbat’, PropertyGenerator::FLAG_CONSTANT), 26 )); 27 echo $foo->generate(); The above results in the following class definition: 1 /** 2 * Sample generated class 3 * 4 * This is a class generated with ZendCodeGenerator. 5 * 6 * @version $Rev:$ 7 * @license New BSD 8 * 9 */ 10 class Foo 11 { 12 13 protected $_bar = ’baz’; 14 15 public $baz = ’bat’; 16 17 const bat = ’foobarbazbat’; 18 19 } 258 Chapter 61. ZendCodeGenerator Examples
  • 299. Zend Framework 2 Documentation, Release 2.3.1dev Generating PHP classes with class methods ZendCodeGeneratorClassGenerator allows you to attach methods with optional content to your classes. Methods may be attached as either arrays or concrete ZendCodeGeneratorMethodGenerator instances. 1 use ZendCodeGeneratorClassGenerator; 2 use ZendCodeGeneratorDocBlockGenerator; 3 use ZendCodeGeneratorDocBlockTag; 4 use ZendCodeGeneratorMethodGenerator; 5 use ZendCodeGeneratorPropertyGenerator; 6 7 $foo = new ClassGenerator(); 8 $docblock = DocBlockGenerator::fromArray(array( 9 ’shortDescription’ => ’Sample generated class’, 10 ’longDescription’ => ’This is a class generated with ZendCodeGenerator.’, 11 ’tags’ => array( 12 array( 13 ’name’ => ’version’, 14 ’description’ => ’$Rev:$’, 15 ), 16 array( 17 ’name’ => ’license’, 18 ’description’ => ’New BSD’, 19 ), 20 ), 21 )); 22 $foo->setName(’Foo’) 23 ->setDocblock($docblock) 24 ->addProperties(array( 25 array(’_bar’, ’baz’, PropertyGenerator::FLAG_PROTECTED), 26 array(’baz’, ’bat’, PropertyGenerator::FLAG_PUBLIC), 27 array(’bat’, ’foobarbazbat’, PropertyGenerator::FLAG_CONSTANT), 28 )) 29 ->addMethods(array( 30 // Method passed as array 31 MethodGenerator::fromArray(array( 32 ’name’ => ’setBar’, 33 ’parameters’ => array(’bar’), 34 ’body’ => ’$this->_bar = $bar;’ . "n" . ’return $this;’, 35 ’docblock’ => DocBlockGenerator::fromArray(array( 36 ’shortDescription’ => ’Set the bar property’, 37 ’longDescription’ => null, 38 ’tags’ => array( 39 new TagParamTag(array( 40 ’paramName’ => ’bar’, 41 ’datatype’ => ’string’ 42 )), 43 new TagReturnTag(array( 44 ’datatype’ => ’string’, 45 )), 46 ), 47 )), 48 )), 49 // Method passed as concrete instance 50 new MethodGenerator( 51 ’getBar’, 52 array(), 53 MethodGenerator::FLAG_PUBLIC, 259
  • 300. Zend Framework 2 Documentation, Release 2.3.1dev 54 ’return $this->_bar;’, 55 DocBlockGenerator::fromArray(array( 56 ’shortDescription’ => ’Retrieve the bar property’, 57 ’longDescription’ => null, 58 ’tags’ => array( 59 new TagReturnTag(array( 60 ’datatype’ => ’string|null’, 61 )), 62 ), 63 )) 64 ), 65 )); 66 67 echo $foo->generate(); The above generates the following output: 1 /** 2 * Sample generated class 3 * 4 * This is a class generated with ZendCodeGenerator. 5 * 6 * @version $Rev:$ 7 * @license New BSD 8 */ 9 class Foo 10 { 11 12 protected $_bar = ’baz’; 13 14 public $baz = ’bat’; 15 16 const bat = ’foobarbazbat’; 17 18 /** 19 * Set the bar property 20 * 21 * @param string bar 22 * @return string 23 */ 24 public function setBar($bar) 25 { 26 $this->_bar = $bar; 27 return $this; 28 } 29 30 /** 31 * Retrieve the bar property 32 * 33 * @return string|null 34 */ 35 public function getBar() 36 { 37 return $this->_bar; 38 } 39 40 } 260 Chapter 61. ZendCodeGenerator Examples
  • 301. Zend Framework 2 Documentation, Release 2.3.1dev Generating PHP files ZendCodeGeneratorFileGenerator can be used to generate the contents of a PHP file. You can include classes as well as arbitrary content body. When attaching classes, you should attach either concrete ZendCodeGeneratorClassGenerator instances or an array defining the class. In the example below, we will assume you’ve defined $foo per one of the class definitions in a previous example. 1 use ZendCodeGeneratorDocBlockGenerator; 2 use ZendCodeGeneratorFileGenerator; 3 4 $file = FileGenerator::fromArray(array( 5 ’classes’ => array($foo), 6 ’docblock’ => DocBlockGenerator::fromArray(array( 7 ’shortDescription’ => ’Foo class file’, 8 ’longDescription’ => null, 9 ’tags’ => array( 10 array( 11 ’name’ => ’license’, 12 ’description’ => ’New BSD’, 13 ), 14 ), 15 )), 16 ’body’ => ’define(’APPLICATION_ENV’, ’testing’);’, 17 )); Calling generate() will generate the code – but not write it to a file. You will need to capture the contents and write them to a file yourself. As an example: 1 $code = $file->generate(); 2 file_put_contents(’Foo.php’, $code); The above will generate the following file: 1 <?php 2 /** 3 * Foo class file 4 * 5 * @license New BSD 6 */ 7 8 /** 9 * Sample generated class 10 * 11 * This is a class generated with ZendCodeGenerator. 12 * 13 * @version $Rev:$ 14 * @license New BSD 15 */ 16 class Foo 17 { 18 19 protected $_bar = ’baz’; 20 21 public $baz = ’bat’; 22 23 const bat = ’foobarbazbat’; 24 25 /** 261
  • 302. Zend Framework 2 Documentation, Release 2.3.1dev 26 * Set the bar property 27 * 28 * @param string bar 29 * @return string 30 */ 31 public function setBar($bar) 32 { 33 $this->_bar = $bar; 34 return $this; 35 } 36 37 /** 38 * Retrieve the bar property 39 * 40 * @return string|null 41 */ 42 public function getBar() 43 { 44 return $this->_bar; 45 } 46 47 } 48 49 define(’APPLICATION_ENV’, ’testing’); Seeding PHP file code generation via reflection You can add PHP code to an existing PHP file using the code generator. To do so, you need to first do reflection on it. The static method fromReflectedFileName() allows you to do this. 1 $generator = ZendCodeGeneratorFileGenerator::fromReflectedFileName($path); 2 $body = $generator->getBody(); 3 $body .= "n$foo->bar();"; 4 file_put_contents($path, $generator->generate()); Seeding PHP class generation via reflection You may add code to an existing class. To do so, first use the static fromReflection() method to map the class into a generator object. From there, you may add additional properties or methods, and then regenerate the class. 1 use ZendCodeGeneratorClassGenerator; 2 use ZendCodeGeneratorDocBlockGenerator; 3 use ZendCodeGeneratorDocBlockTag; 4 use ZendCodeGeneratorMethodGenerator; 5 use ZendCodeReflectionClassReflection; 6 7 $generator = ClassGenerator::fromReflection( 8 new ClassReflection($class) 9 ); 10 $generator->addMethod( 11 ’setBaz’, 12 array(’baz’), 13 MethodGenerator::FLAG_PUBLIC, 14 ’$this->_baz = $baz;’ . "n" . ’return $this;’, 15 DocBlockGenerator::fromArray(array( 262 Chapter 61. ZendCodeGenerator Examples
  • 303. Zend Framework 2 Documentation, Release 2.3.1dev 16 ’shortDescription’ => ’Set the baz property’, 17 ’longDescription’ => null, 18 ’tags’ => array( 19 new TagParamTag(array( 20 ’paramName’ => ’baz’, 21 ’datatype’ => ’string’ 22 )), 23 new TagReturnTag(array( 24 ’datatype’ => ’string’, 25 )), 26 ), 27 )) 28 ); 29 $code = $generator->generate(); 263
  • 304. Zend Framework 2 Documentation, Release 2.3.1dev 264 Chapter 61. ZendCodeGenerator Examples
  • 305. CHAPTER 62 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. 62.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 // An array of configuration data is given 2 $configArray = array( 3 ’webhost’ => ’www.example.com’, 4 ’database’ => array( 5 ’adapter’ => ’pdo_mysql’, 6 ’params’ => array( 7 ’host’ => ’db.example.com’, 8 ’username’ => ’dbuser’, 9 ’password’ => ’secret’, 10 ’dbname’ => ’mydatabase’ 11 ) 12 ) 13 ); 14 15 // Create the object-oriented wrapper using the configuration data 16 $config = new ZendConfigConfig($configArray); 17 18 // Print a configuration datum (results in ’www.example.com’) 19 echo $config->webhost; As illustrated in the example above, ZendConfigConfig provides nested object property syntax to access con- figuration 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: 1 $host = $config->database->get(’host’, ’localhost’); 265
  • 306. Zend Framework 2 Documentation, Release 2.3.1dev 62.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 // config.php 2 return array( 3 ’webhost’ => ’www.example.com’, 4 ’database’ => array( 5 ’adapter’ => ’pdo_mysql’, 6 ’params’ => array( 7 ’host’ => ’db.example.com’, 8 ’username’ => ’dbuser’, 9 ’password’ => ’secret’, 10 ’dbname’ => ’mydatabase’ 11 ) 12 ) 13 ); 1 // Consumes the configuration array 2 $config = new ZendConfigConfig(include ’config.php’); 3 4 // Print a configuration datum (results in ’www.example.com’) 5 echo $config->webhost; 266 Chapter 62. Introduction to ZendConfig
  • 307. CHAPTER 63 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 configu- ration 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 config- uration 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 behav- ior 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() func- tion. 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. 267
  • 308. Zend Framework 2 Documentation, Release 2.3.1dev 268 Chapter 63. Theory of Operation
  • 309. CHAPTER 64 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. 64.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 $reader = new ZendConfigReaderIni(); 2 $reader->setNestSeparator(’-’); 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: 269
  • 310. Zend Framework 2 Documentation, Release 2.3.1dev 1 webhost = ’www.example.com’ 2 database.adapter = ’pdo_mysql’ 3 database.params.host = ’db.example.com’ 4 database.params.username = ’dbuser’ 5 database.params.password = ’secret’ 6 database.params.dbname = ’dbproduction’ We can use the ZendConfigReaderIni to read this INI file: 1 $reader = new ZendConfigReaderIni(); 2 $data = $reader->fromFile(’/path/to/config.ini’); 3 4 echo $data[’webhost’] // prints "www.example.com" 5 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 database.adapter = ’pdo_mysql’ 2 database.params.host = ’db.example.com’ 3 database.params.username = ’dbuser’ 4 database.params.password = ’secret’ 5 database.params.dbname = ’dbproduction’ We can include this configuration in another INI file, for instance: 1 webhost = ’www.example.com’ 2 @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 adapter = ’pdo_mysql’ 2 params.host = ’db.example.com’ 3 params.username = ’dbuser’ 4 params.password = ’secret’ 5 params.dbname = ’dbproduction’ And assign the @include as subelement of the database value: 1 webhost = ’www.example.com’ 2 database.@include = ’database.ini’ 64.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. 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 <?xml version="1.0" encoding="utf-8"?>?> 2 <config> 3 <webhost>www.example.com</webhost> 270 Chapter 64. ZendConfigReader
  • 311. Zend Framework 2 Documentation, Release 2.3.1dev 4 <database> 5 <adapter value="pdo_mysql"/> 6 <params> 7 <host value="db.example.com"/> 8 <username value="dbuser"/> 9 <password value="secret"/> 10 <dbname value="dbproduction"/> 11 </params> 12 </database> 13 </config> We can use the ZendConfigReaderXml to read this XML file: 1 $reader = new ZendConfigReaderXml(); 2 $data = $reader->fromFile(’/path/to/config.xml’); 3 4 echo $data[’webhost’] // prints "www.example.com" 5 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 <?xml version="1.0" encoding="utf-8"?> 2 <config> 3 <database> 4 <adapter>pdo_mysql</adapter> 5 <params> 6 <host>db.example.com</host> 7 <username>dbuser</username> 8 <password>secret</password> 9 <dbname>dbproduction</dbname> 10 </params> 11 </database> 12 </config> We can include this configuration in another XML file, for instance: 1 <?xml version="1.0" encoding="utf-8"?> 2 <config xmlns:xi="http://www.w3.org/2001/XInclude"> 3 <webhost>www.example.com</webhost> 4 <xi:include href="database.xml"/> 5 </config> The syntax to include an XML file in a specific element is <xi:include href="file-to-include.xml"/> 64.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: 64.3. ZendConfigReaderJson 271
  • 312. Zend Framework 2 Documentation, Release 2.3.1dev 1 { 2 "webhost" : "www.example.com", 3 "database" : { 4 "adapter" : "pdo_mysql", 5 "params" : { 6 "host" : "db.example.com", 7 "username" : "dbuser", 8 "password" : "secret", 9 "dbname" : "dbproduction" 10 } 11 } 12 } We can use the ZendConfigReaderJson to read this JSON file: 1 $reader = new ZendConfigReaderJson(); 2 $data = $reader->fromFile(’/path/to/config.json’); 3 4 echo $data[’webhost’] // prints "www.example.com" 5 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 { 2 "database" : { 3 "adapter" : "pdo_mysql", 4 "params" : { 5 "host" : "db.example.com", 6 "username" : "dbuser", 7 "password" : "secret", 8 "dbname" : "dbproduction" 9 } 10 } 11 } We can include this configuration in another JSON file, for instance: 1 { 2 "webhost" : "www.example.com", 3 "@include" : "database.json" 4 } 64.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 webhost: www.example.com 2 database: 272 Chapter 64. ZendConfigReader
  • 313. Zend Framework 2 Documentation, Release 2.3.1dev 3 adapter: pdo_mysql 4 params: 5 host: db.example.com 6 username: dbuser 7 password: secret 8 dbname: dbproduction We can use the ZendConfigReaderYaml to read this YAML file: 1 $reader = new ZendConfigReaderYaml(); 2 $data = $reader->fromFile(’/path/to/config.yaml’); 3 4 echo $data[’webhost’] // prints "www.example.com" 5 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 // include the Spyc library 2 require_once (’path/to/spyc.php’); 3 4 $reader = new ZendConfigReaderYaml(array(’Spyc’,’YAMLLoadString’)); 5 $data = $reader->fromFile(’/path/to/config.yaml’); 6 7 echo $data[’webhost’] // prints "www.example.com" 8 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 database: 2 adapter: pdo_mysql 3 params: 4 host: db.example.com 5 username: dbuser 6 password: secret 7 dbname: dbproduction We can include this configuration in another YAML file, for instance: webhost: www.example.com @include: database.yaml 64.4. ZendConfigReaderYaml 273
  • 314. Zend Framework 2 Documentation, Release 2.3.1dev 274 Chapter 64. ZendConfigReader
  • 315. CHAPTER 65 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 65.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 de- fines 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 // Create the config object 2 $config = new ZendConfigConfig(array(), true); 3 $config->production = array(); 4 5 $config->production->webhost = ’www.example.com’; 6 $config->production->database = array(); 7 $config->production->database->params = array(); 8 $config->production->database->params->host = ’localhost’; 9 $config->production->database->params->username = ’production’; 275
  • 316. Zend Framework 2 Documentation, Release 2.3.1dev 10 $config->production->database->params->password = ’secret’; 11 $config->production->database->params->dbname = ’dbproduction’; 12 13 $writer = new ZendConfigWriterIni(); 14 echo $writer->toString($config); The result of this code is an INI string contains the following values: 1 [production] 2 webhost = "www.example.com" 3 database.params.host = "localhost" 4 database.params.username = "production" 5 database.params.password = "secret" 6 database.params.dbname = "dbproduction" You can use the method toFile() to store the INI data in a file. 65.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 // Create the config object 2 $config = new ZendConfigConfig(array(), true); 3 $config->production = array(); 4 5 $config->production->webhost = ’www.example.com’; 6 $config->production->database = array(); 7 $config->production->database->params = array(); 8 $config->production->database->params->host = ’localhost’; 9 $config->production->database->params->username = ’production’; 10 $config->production->database->params->password = ’secret’; 11 $config->production->database->params->dbname = ’dbproduction’; 12 13 $writer = new ZendConfigWriterXml(); 14 echo $writer->toString($config); The result of this code is an XML string contains the following data: 1 <?xml version="1.0" encoding="UTF-8"?> 2 <zend-config> 3 <production> 4 <webhost>www.example.com</webhost> 5 <database> 6 <params> 7 <host>localhost</host> 8 <username>production</username> 9 <password>secret</password> 10 <dbname>dbproduction</dbname> 11 </params> 12 </database> 276 Chapter 65. ZendConfigWriter
  • 317. Zend Framework 2 Documentation, Release 2.3.1dev 13 </production> 14 </zend-config> You can use the method toFile() to store the XML data in a file. 65.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 // Create the config object 2 $config = new ZendConfigConfig(array(), true); 3 $config->production = array(); 4 5 $config->production->webhost = ’www.example.com’; 6 $config->production->database = array(); 7 $config->production->database->params = array(); 8 $config->production->database->params->host = ’localhost’; 9 $config->production->database->params->username = ’production’; 10 $config->production->database->params->password = ’secret’; 11 $config->production->database->params->dbname = ’dbproduction’; 12 13 $writer = new ZendConfigWriterPhpArray(); 14 echo $writer->toString($config); The result of this code is a PHP script that returns an array as follow: 1 <?php 2 return array ( 3 ’production’ => 4 array ( 5 ’webhost’ => ’www.example.com’, 6 ’database’ => 7 array ( 8 ’params’ => 9 array ( 10 ’host’ => ’localhost’, 11 ’username’ => ’production’, 12 ’password’ => ’secret’, 13 ’dbname’ => ’dbproduction’, 14 ), 15 ), 16 ), 17 ); You can use the method toFile() to store the PHP script in a file. 65.3. ZendConfigWriterPhpArray 277
  • 318. Zend Framework 2 Documentation, Release 2.3.1dev 65.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 // Create the config object 2 $config = new ZendConfigConfig(array(), true); 3 $config->production = array(); 4 5 $config->production->webhost = ’www.example.com’; 6 $config->production->database = array(); 7 $config->production->database->params = array(); 8 $config->production->database->params->host = ’localhost’; 9 $config->production->database->params->username = ’production’; 10 $config->production->database->params->password = ’secret’; 11 $config->production->database->params->dbname = ’dbproduction’; 12 13 $writer = new ZendConfigWriterJson(); 14 echo $writer->toString($config); The result of this code is a JSON string contains the following values: 1 { "webhost" : "www.example.com", 2 "database" : { 3 "params" : { 4 "host" : "localhost", 5 "username" : "production", 6 "password" : "secret", 7 "dbname" : "dbproduction" 8 } 9 } 10 } 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. 65.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: 278 Chapter 65. ZendConfigWriter
  • 319. Zend Framework 2 Documentation, Release 2.3.1dev 1 // Create the config object 2 $config = new ZendConfigConfig(array(), true); 3 $config->production = array(); 4 5 $config->production->webhost = ’www.example.com’; 6 $config->production->database = array(); 7 $config->production->database->params = array(); 8 $config->production->database->params->host = ’localhost’; 9 $config->production->database->params->username = ’production’; 10 $config->production->database->params->password = ’secret’; 11 $config->production->database->params->dbname = ’dbproduction’; 12 13 $writer = new ZendConfigWriterYaml(); 14 echo $writer->toString($config); The result of this code is a YAML string contains the following values: 1 webhost: www.example.com 2 database: 3 params: 4 host: localhost 5 username: production 6 password: secret 7 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 // include the Spyc library 2 require_once (’path/to/spyc.php’); 3 4 $writer = new ZendConfigWriterYaml(array(’Spyc’,’YAMLDump’)); 5 echo $writer->toString($config); 65.5. ZendConfigWriterYaml 279
  • 320. Zend Framework 2 Documentation, Release 2.3.1dev 280 Chapter 65. ZendConfigWriter
  • 321. CHAPTER 66 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: translate configuration values in other languages using ZendI18nTranslator; Below we reported some examples for each type of processor. 66.1 ZendConfigProcessorConstant Using ZendConfigProcessorConstant This example illustrates the basic use of ZendConfigProcessorConstant: 1 define (’TEST_CONST’, ’bar’); 2 // set true to ZendConfigConfig to allow modifications 3 $config = new ZendConfigConfig(array(’foo’ => ’TEST_CONST’), true); 4 $processor = new ZendConfigProcessorConstant(); 5 6 echo $config->foo . ’,’; 7 $processor->process($config); 8 echo $config->foo; This example returns the output: TEST_CONST, bar.. 66.2 ZendConfigProcessorFilter Using ZendConfigProcessorFilter This example illustrates the basic use of ZendConfigProcessorFilter: 281
  • 322. Zend Framework 2 Documentation, Release 2.3.1dev 1 use ZendFilterStringToUpper; 2 use ZendConfigProcessorFilter as FilterProcessor; 3 use ZendConfigConfig; 4 5 $config = new Config(array (’foo’ => ’bar’), true); 6 $upper = new StringToUpper(); 7 8 $upperProcessor = new FilterProcessor($upper); 9 10 echo $config->foo . ’,’; 11 $upperProcessor->process($config); 12 echo $config->foo; This example returns the output: bar,BAR. 66.3 ZendConfigProcessorQueue Using ZendConfigProcessorQueue This example illustrates the basic use of ZendConfigProcessorQueue: 1 use ZendFilterStringToLower; 2 use ZendFilterStringToUpper; 3 use ZendConfigProcessorFilter as FilterProcessor; 4 use ZendConfigProcessorQueue; 5 use ZendConfigConfig; 6 7 $config = new Config(array (’foo’ => ’bar’), true); 8 $upper = new StringToUpper(); 9 $lower = new StringToLower(); 10 11 $lowerProcessor = new FilterProcessor($lower); 12 $upperProcessor = new FilterProcessor($upper); 13 14 $queue = new Queue(); 15 $queue->insert($upperProcessor); 16 $queue->insert($lowerProcessor); 17 $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). 66.4 ZendConfigProcessorToken Using ZendConfigProcessorToken This example illustrates the basic use of ZendConfigProcessorToken: 1 // set the Config to true to allow modifications 2 $config = new Config(array(’foo’ => ’Value is TOKEN’), true); 3 $processor = new TokenProcessor(); 4 282 Chapter 66. ZendConfigProcessor
  • 323. Zend Framework 2 Documentation, Release 2.3.1dev 5 $processor->addToken(’TOKEN’, ’bar’); 6 echo $config->foo . ’,’; 7 $processor->process($config); 8 echo $config->foo; This example returns the output: Value is TOKEN,Value is bar. 66.5 ZendConfigProcessorTranslator Using ZendConfigProcessorTranslator This example illustrates the basic use of ZendConfigProcessorTranslator: 1 use ZendConfigConfig; 2 use ZendConfigProcessorTranslator as TranslatorProcessor; 3 use ZendI18nTranslatorTranslator; 4 5 $config = new Config(array(’animal’ => ’dog’), true); 6 7 /* 8 * The following mapping would exist for the translation 9 * loader you provide to the translator instance 10 * $italian = array( 11 * ’dog’ => ’cane’ 12 * ); 13 */ 14 15 $translator = new Translator(); 16 // ... configure the translator ... 17 $processor = new TranslatorProcessor($translator); 18 19 echo "English: {$config->animal}, "; 20 $processor->process($config); 21 echo "Italian: {$config->animal}"; This example returns the output: English: dog, Italian: cane. 66.5. ZendConfigProcessorTranslator 283
  • 324. Zend Framework 2 Documentation, Release 2.3.1dev 284 Chapter 66. ZendConfigProcessor
  • 325. CHAPTER 67 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 configu- rations 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. 67.1 Loading configuration file The next example illustrates how to load a single configuration file 1 //Load a php file as array 2 $config = ZendConfigFactory::fromFile(__DIR__.’/config/my.config.php’); 3 4 //Load a xml file as Config object 5 $config = ZendConfigFactory::fromFile(__DIR__.’/config/my.config.xml’, true); For merging multiple configuration files 67.2 Storing configuration file Sometimes you want to store the configuration to a file. Also this is really easy to do 285
  • 326. Zend Framework 2 Documentation, Release 2.3.1dev 286 Chapter 67. The Factory
  • 327. CHAPTER 68 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. 68.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> This simple route definition expects exactly 3 arguments: a literal “user”, literal “resetpassword” followed by a pa- rameter we’re calling “userEmail”. Let’s assume we also accept one optional parameter, that will turn on verbose operation: 287
  • 328. Zend Framework 2 Documentation, Release 2.3.1dev 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 array( 2 ’router’ => array( 3 ’routes’ => array( 4 // HTTP routes are defined here 5 ) 6 ), 7 8 ’console’ => array( 9 ’router’ => array( 10 ’routes’ => array( 11 // Console routes go here 12 ) 13 ) 14 ), 15 ) Let’s create our console route and point it to ApplicationControllerIndexController::resetpasswordAction() 1 // we could define routes for ApplicationControllerIndexController in Application module config fil 2 // which is usually located at modules/application/config/module.config.php 3 array( 4 ’console’ => array( 5 ’router’ => array( 6 ’routes’ => array( 7 ’user-reset-password’ => array( 8 ’options’ => array( 9 ’route’ => ’user resetpassword [--verbose|-v] <userEmail>’, 10 ’defaults’ => array( 11 ’controller’ => ’ApplicationControllerIndex’, 12 ’action’ => ’password’ 13 ) 14 ) 15 ) 16 ) 17 ) 18 ) 19 ) See also: To learn more about console routes and how to use them, please read this chapter: Console routes and routing 288 Chapter 68. Introduction to ZendConsole
  • 329. Zend Framework 2 Documentation, Release 2.3.1dev 68.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 <?php 2 namespace ApplicationController; 3 4 use ZendMvcControllerAbstractActionController; 5 use ZendViewModelViewModel; 6 use ZendConsoleRequest as ConsoleRequest; 7 use ZendMathRand; 8 9 class IndexController extends AbstractActionController 10 { 11 public function indexAction() 12 { 13 return new ViewModel(); // display standard index page 14 } 15 16 public function resetpasswordAction() 17 { 18 $request = $this->getRequest(); 19 20 // Make sure that we are running in a console and the user has not tricked our 21 // application into running this action from a public web server. 22 if (!$request instanceof ConsoleRequest){ 23 throw new RuntimeException(’You can only use this action from a console!’); 24 } 25 26 // Get user email from console and check if the user used --verbose or -v flag 27 $userEmail = $request->getParam(’userEmail’); 28 $verbose = $request->getParam(’verbose’); 29 30 // reset new password 31 $newPassword = Rand::getString(16); 32 33 // Fetch the user and change his password, then email him ... 34 // [...] 35 36 if (!$verbose) { 37 return "Done! $userEmail has received an email with his new password.n"; 38 }else{ 39 return "Done! New password for user $userEmail is ’$newPassword’. It has also been emaile 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. 68.2. Handling console requests 289
  • 330. Zend Framework 2 Documentation, Release 2.3.1dev 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 68.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 use ZendModuleManagerFeatureAutoloaderProviderInterface; 6 use ZendModuleManagerFeatureConfigProviderInterface; 7 use ZendModuleManagerFeatureConsoleUsageProviderInterface; 8 use ZendConsoleAdapterAdapterInterface as Console; 9 10 class Module implements 11 AutoloaderProviderInterface, 12 ConfigProviderInterface, 13 ConsoleUsageProviderInterface // <- our module implement this feature and provides console usag 14 { 15 public function getConfig() 16 { 17 // [...] 18 } 19 20 public function getAutoloaderConfig() 21 { 22 // [...] 23 } 24 25 public function getConsoleUsage(Console $console) 26 { 27 return array( 28 // Describe available commands 29 ’user resetpassword [--verbose|-v] EMAIL’ => ’Reset password for a user’, 30 31 // Describe expected parameters 32 array( ’EMAIL’, ’Email of the user for a password reset’ ), 33 array( ’--verbose|-v’, ’(optional) turn on verbose mode’ ), 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. Note: The order of usage info displayed in the console is the order modules load. If you want your application to 290 Chapter 68. Introduction to ZendConsole
  • 331. Zend Framework 2 Documentation, Release 2.3.1dev 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 68.3. Adding console usage info 291
  • 332. Zend Framework 2 Documentation, Release 2.3.1dev 292 Chapter 68. Introduction to ZendConsole
  • 333. CHAPTER 69 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. 69.1 Router configuration All Console Routes are automatically read from the following configuration location: 1 // This can sit inside of modules/Application/config/module.config.php or any other module’s config. 2 array( 3 ’router’ => array( 4 ’routes’ => array( 5 // HTTP routes are here 6 ) 7 ), 8 9 ’console’ => array( 10 ’router’ => array( 11 ’routes’ => array( 12 // Console routes go here 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: 1 // inside config.console.router.routes: 2 // [...] 293
  • 334. Zend Framework 2 Documentation, Release 2.3.1dev 3 ’my-first-route’ => array( 4 ’type’ => ’simple’, // <- simple route is created by default, we can skip that 5 ’options’ => array( 6 ’route’ => ’foo bar’, 7 ’defaults’ => array( 8 ’controller’ => ’ApplicationControllerIndex’, 9 ’action’ => ’password’ 10 ) 11 ) 12 ) 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. 69.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]) 69.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 ’show-users’ => array( 2 ’options’ => array( 3 ’route’ => ’show users’, 4 ’defaults’ => array( 5 ’controller’ => ’ApplicationControllerUsers’, 6 ’action’ => ’show’ 7 ) 8 ) 9 ) 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: 1 ’show-users’ => array( 2 ’options’ => array( 3 ’route’ => ’show [all] users’, 294 Chapter 69. Console routes and routing
  • 335. Zend Framework 2 Documentation, Release 2.3.1dev 4 ’defaults’ => array( 5 ’controller’ => ’ApplicationControllerUsers’, 6 ’action’ => ’show’ 7 ) 8 ) 9 ) Now this route will match for both of these commands: > zf show users > zf show all users We can also provide parameter alternative: 1 ’show-users’ => array( 2 ’options’ => array( 3 ’route’ => ’show [all|deleted|locked|admin] users’, 4 ’defaults’ => array( 5 ’controller’ => ’ApplicationControllerUsers’, 6 ’action’ => ’show’ 7 ) 8 ) 9 ) 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 69.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 ’check-users’ => array( 2 ’options’ => array( 3 ’route’ => ’check users [--verbose] [--fast] [--thorough]’, 4 ’defaults’ => array( 5 ’controller’ => ’ApplicationControllerUsers’, 6 ’action’ => ’check’ 7 ) 8 ) 9 ) This route will match for commands like: > zf check users > zf check users --fast 69.2. Basic route 295
  • 336. Zend Framework 2 Documentation, Release 2.3.1dev > zf check users --verbose --thorough > zf check users --thorough --fast We can also define one or more mandatory long flags and group them as an alternative: 1 ’check-users’ => array( 2 ’options’ => array( 3 ’route’ => ’check users (--suspicious|--expired) [--verbose] [--fast] [--thorough]’, 4 ’defaults’ => array( 5 ’controller’ => ’ApplicationControllerUsers’, 6 ’action’ => ’check’ 7 ) 8 ) 9 ) 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 ’check-users’ => array( 2 ’options’ => array( 3 ’route’ => ’check users [--verbose|-v] [--fast|-f] [--thorough|-t]’, 4 ’defaults’ => array( 5 ’controller’ => ’ApplicationControllerUsers’, 6 ’action’ => ’check’ 7 ) 8 ) 9 ) Now we can use short versions of our flags: > zf check users -f > zf check users -v --thorough > zf check users -t -f -v 69.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 ’delete-user’ => array( 2 ’options’ => array( 3 ’route’ => ’delete user <userEmail>’, 4 ’defaults’ => array( 5 ’controller’ => ’ApplicationControllerUsers’, 6 ’action’ => ’delete’ 7 ) 8 ) 9 ) This route will match for commands like: 296 Chapter 69. Console routes and routing
  • 337. Zend Framework 2 Documentation, Release 2.3.1dev > 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 ’delete-user’ => array( 2 ’options’ => array( 3 ’route’ => ’delete user [<userEmail>]’, 4 ’defaults’ => array( 5 ’controller’ => ’ApplicationControllerUsers’, 6 ’action’ => ’delete’ 7 ) 8 ) 9 ) 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 ’create-user’ => array( 2 ’options’ => array( 3 ’route’ => ’create user <firstName> <lastName> <email> <position>’, 4 ’defaults’ => array( 5 ’controller’ => ’ApplicationControllerUsers’, 6 ’action’ => ’create’ 7 ) 8 ) 9 ) 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" 69.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 ’find-user’ => array( 2 ’options’ => array( 3 ’route’ => ’find user [--id=] [--firstName=] [--lastName=] [--email=] [--position=] ’, 4 ’defaults’ => array( 5 ’controller’ => ’ApplicationControllerUsers’, 6 ’action’ => ’find’ 7 ) 69.2. Basic route 297
  • 338. Zend Framework 2 Documentation, Release 2.3.1dev 8 ) 9 ) This route will match for any of the following routes: > zf find user > zf find user --id 29110 > zf find user --id=29110 > zf find user --firstName=Johny --lastName=Bravo > zf find user --lastName Bravo --firstName Johny > zf find user --position=Executive --firstName=Bob > zf find user --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 ’rename-user’ => array( 2 ’options’ => array( 3 ’route’ => ’rename user --id= [--firstName=] [--lastName=]’, 4 ’defaults’ => array( 5 ’controller’ => ’ApplicationControllerUsers’, 6 ’action’ => ’rename’ 7 ) 8 ) 9 ) 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 69.3 Catchall route This special route will catch all console requests, regardless of the parameters provided. 1 ’default-route’ => array( 2 ’type’ => ’catchall’, 3 ’options’ => array( 4 ’route’ => ’’, 5 ’defaults’ => array( 6 ’controller’ => ’ApplicationControllerIndex’, 7 ’action’ => ’consoledefault’ 8 ) 9 ) 10 ) 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. 298 Chapter 69. Console routes and routing
  • 339. Zend Framework 2 Documentation, Release 2.3.1dev 69.4 Console routes cheat-sheet Param type Example route definition Explanation Literal params Literal foo bar “foo” followed by “bar” Literal alternative foo (bar|baz) “foo” followed by “bar” or “baz” Literal, optional foo [bar] “foo”, optional “bar” Literal, optional alternative foo [bar|baz] “foo”, optional “bar” or “baz” Flags Flag long foo --bar “foo” as first parameter, “–bar” flag before or after Flag long, optional foo [--bar] “foo” as first parameter, optional “–bar” flag before or after Flag long, optional, alternative foo [--bar|--baz] “foo” as first parameter, optional “–bar” or “–baz”, before or after Flag short foo -b “foo” as first parameter, “-b” flag before or after Flag short, optional foo [-b] “foo” as first parameter, optional “-b” flag before or after Flag short, optional, alternative foo [-b|-z] “foo” as first parameter, optional “-b” or “-z”, before or after Flag long/short alternative foo [--bar|-b] “foo” as first parameter, optional “–bar” or “-b” before or after Value parameters Value positional param foo <bar> “foo” followed by any text (stored as “bar” param) Value positional param, optional foo [<bar>] “foo”, optionally followed by any text (stored as “bar” param) Value Flag foo --bar= “foo” as first parameter, “–bar” with a value, before or after Value Flag, optional foo [--bar=] “foo” as first parameter, optionally “–bar” with a value, before or after Parameter groups Literal params group foo (bar|baz):myParam “foo” followed by “bar” or “baz” (stored as “myParam” param) Literal optional params group foo [bar|baz]:myParam “foo” followed by optional “bar” or “baz” (stored as “myParam” param) Long flags group foo (--bar|--baz):myParam “foo”, “bar” or “baz” flag before or after (stored as “myParam” param) Long optional flags group foo [--bar|--baz]:myParam “foo”, optional “bar” or “baz” flag before or after (as “myParam” param) Short flags group foo (-b|-z):myParam “foo”, “-b” or “-z” flag before or after (stored as “myParam” param) Short optional flags group foo [-b|-z]:myParam “foo”, optional “-b” or “-z” flag before or after (stored as “myParam” param) 69.4. Console routes cheat-sheet 299
  • 340. Zend Framework 2 Documentation, Release 2.3.1dev 300 Chapter 69. Console routes and routing
  • 341. CHAPTER 70 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. 70.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. 1 // modules/Application/Module.php 2 <?php 3 namespace Application; 4 5 use ZendModuleManagerFeatureConsoleBannerProviderInterface; 6 use ZendConsoleAdapterAdapterInterface as Console; 7 8 class Module implements ConsoleBannerProviderInterface 9 { 10 /** 11 * This method is defined in ConsoleBannerProviderInterface 12 */ 13 public function getConsoleBanner(Console $console) 14 { 15 return ’MyModule 0.0.1’; 16 } 17 } 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. 301
  • 342. Zend Framework 2 Documentation, Release 2.3.1dev After running our application, we’ll see our newly created banner. Let’s create and load second module that provides a banner. 1 <?php 2 // config/application.config.php 3 return array( 4 ’modules’ => array( 5 ’Application’, 6 ’User’, // < load user module in modules/User 7 ), User module will add-on a short info about itself: 1 // modules/User/Module.php 2 <?php 3 namespace User; 4 5 use ZendModuleManagerFeatureConsoleBannerProviderInterface; 6 use ZendConsoleAdapterAdapterInterface as Console; 7 8 class Module implements ConsoleBannerProviderInterface 9 { 10 /** 11 * This method is defined in ConsoleBannerProviderInterface 12 */ 13 public function getConsoleBanner(Console $console){ 14 return "User Module 0.0.1"; 15 } 16 } 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. 70.2 Basic usage In order to display usage information, our Module class has to implement ZendModuleManagerFeatureConsoleUsageProviderInterface. Let’s modify our example and add new method: 1 // modules/Application/Module.php 2 <?php 3 namespace Application; 4 5 use ZendModuleManagerFeatureConsoleBannerProviderInterface; 6 use ZendModuleManagerFeatureConsoleUsageProviderInterface; 7 use ZendConsoleAdapterAdapterInterface as Console; 8 9 class Module implements ConsoleBannerProviderInterface, ConsoleUsageProviderInterface 10 { 11 public function getConsoleBanner(Console $console){ // ... } 302 Chapter 70. Console-aware modules
  • 343. Zend Framework 2 Documentation, Release 2.3.1dev 12 13 /** 14 * This method is defined in ConsoleUsageProviderInterface 15 */ 16 public function getConsoleUsage(Console $console) 17 { 18 return array( 19 ’show stats’ => ’Show application statistics’, 20 ’run cron’ => ’Run automated jobs’, 21 ’(enable|disable) debug’ => ’Enable or disable debug mode for the application.’ 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 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. 70.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 public function getConsoleUsage(Console $console) 2 { 3 return ’User module expects exactly one argument - user name. It will display information for thi 4 } 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(). 70.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 public function getConsoleUsage(Console $console) 2 { 3 return array( 4 ’delete user <userEmail>’ => ’Delete user with email <userEmail>’, 5 ’disable user <userEmail>’ => ’Disable user with email <userEmail>’, 70.2. Basic usage 303
  • 344. Zend Framework 2 Documentation, Release 2.3.1dev 6 ’list [all|disabled] users’ => ’Show a list of users’, 7 ’find user [--email=] [--name=]’ => ’Attempt to find a user by email or name’, 8 ); 9 } 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. 70.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: 1 public function getConsoleUsage(Console $console) 2 { 3 return array( 4 array( ’<userEmail>’ , ’email of the user’ ), 5 array( ’--verbose’ , ’Turn on verbose mode’ ), 6 array( ’--quick’ , ’Perform a "quick" operation’ ), 7 array( ’-v’ , ’Same as --verbose’ ), 8 array( ’-w’ , ’Wide output’) 9 ); 10 } Using this method, we can display more than 2 columns of information, for example: 1 public function getConsoleUsage(Console $console) 2 { 3 return array( 4 array( ’<userEmail>’ , ’user email’ , ’Full email address of the user to find.’ ), 5 array( ’--verbose’ , ’verbose mode’ , ’Display additional information during processin 6 array( ’--quick’ , ’"quick" operation’ , ’Do not check integrity, just make changes and f 7 array( ’-v’ , ’Same as --verbose’ , ’Display additional information during processin 8 array( ’-w’ , ’wide output’ , ’When listing users, use the whole available scr 9 ); 10 } 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. 304 Chapter 70. Console-aware modules
  • 345. Zend Framework 2 Documentation, Release 2.3.1dev 70.2.4 Mixing styles You can use mix together all of the above styles to provide comprehensive usage information, for example: 1 public function getConsoleUsage(Console $console) 2 { 3 return array( 4 ’Finding and listing users’, 5 ’list [all|disabled] users [-w]’ => ’Show a list of users’, 6 ’find user [--email=] [--name=]’ => ’Attempt to find a user by email or name’, 7 8 array(’[all|disabled]’, ’Display all users or only disabled accounts’), 9 array(’--email=EMAIL’, ’Email of the user to find’), 10 array(’--name=NAME’, ’Full name of the user to find.’), 11 array(’-w’, ’Wide output - When listing users use the whole available screen w 12 13 ’Manipulation of user database:’, 14 ’delete user <userEmail> [--verbose|-v] [--quick]’ => ’Delete user with email <userEmail>’, 15 ’disable user <userEmail> [--verbose|-v]’ => ’Disable user with email <userEmail>’, 16 17 array( ’<userEmail>’ , ’user email’ , ’Full email address of the user to change.’ ), 18 array( ’--verbose’ , ’verbose mode’ , ’Display additional information during processin 19 array( ’--quick’ , ’"quick" operation’ , ’Do not check integrity, just make changes and f 20 array( ’-v’ , ’Same as --verbose’ , ’Display additional information during processin 21 22 ); 23 } 70.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. 70.3. Best practices 305
  • 346. Zend Framework 2 Documentation, Release 2.3.1dev 306 Chapter 70. Console-aware modules
  • 347. CHAPTER 71 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 71.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 Service- Manager, 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 // FILE: modules/Application/config/module.config.php 2 array( 3 ’router’ => array( 4 ’routes’ => array( 5 // HTTP routes are here 6 ) 7 ), 8 9 ’console’ => array( 10 ’router’ => array( 11 ’routes’ => array( 12 ’list-users’ => array( 13 ’options’ => array( 14 ’route’ => ’show [all|disabled|deleted]:mode users [--verbose|-v]’, 15 ’defaults’ => array( 16 ’controller’ => ’ApplicationControllerIndex’, 17 ’action’ => ’show-users’ 307
  • 348. Zend Framework 2 Documentation, Release 2.3.1dev 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 <?php 2 namespace ApplicationController; 3 4 use ZendMvcControllerAbstractActionController; 5 use ZendViewModelViewModel; 6 7 class IndexController extends AbstractActionController 8 { 9 public function indexAction() 10 { 11 return new ViewModel(); // display standard index page 12 } 13 14 public function showUsersAction() 15 { 16 $request = $this->getRequest(); 17 18 // Check verbose flag 19 $verbose = $request->getParam(’verbose’) || $request->getParam(’v’); 20 21 // Check mode 22 $mode = $request->getParam(’mode’, ’all’); // defaults to ’all’ 23 24 $users = array(); 25 switch ($mode) { 26 case ’disabled’: 27 $users = $this->getServiceLocator()->get(’users’)->fetchDisabledUsers(); 28 break; 29 case ’deleted’: 30 $users = $this->getServiceLocator()->get(’users’)->fetchDeletedUsers(); 31 break; 32 case ’all’: 33 default: 34 $users = $this->getServiceLocator()->get(’users’)->fetchAllUsers(); 35 break; 36 } 37 } 38 } 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. 308 Chapter 71. Console-aware action controllers
  • 349. Zend Framework 2 Documentation, Release 2.3.1dev 71.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 public function showUsersAction() 2 { 3 $request = $this->getRequest(); 4 5 // Check verbose flag 6 $verbose = $request->getParam(’verbose’) || $request->getParam(’v’); 7 8 // Check mode 9 $mode = $request->getParam(’mode’, ’all’); // defaults to ’all’ 10 11 $users = array(); 12 switch ($mode) { 13 case ’disabled’: 14 $users = $this->getServiceLocator()->get(’users’)->fetchDisabledUsers(); 15 break; 16 case ’deleted’: 17 $users = $this->getServiceLocator()->get(’users’)->fetchDeletedUsers(); 18 break; 19 case ’all’: 20 default: 21 $users = $this->getServiceLocator()->get(’users’)->fetchAllUsers(); 22 break; 23 } 24 25 if (count($users) == 0) { 26 // Show an error message in the console 27 return "There are no users in the databasen"; 28 } 29 30 $result = ’’; 31 32 foreach ($users as $user) { 33 $result .= $user->name . ’ ’ . $user->email . "n"; 34 } 35 36 return $result; // show it in the console 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. 71.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: 71.2. Sending output to console 309
  • 350. Zend Framework 2 Documentation, Release 2.3.1dev 1 namespace ApplicationController; 2 3 use ZendMvcControllerAbstractActionController; 4 use ZendViewModelViewModel; 5 use ZendConsoleRequest as ConsoleRequest; 6 use RuntimeException; 7 8 class IndexController extends AbstractActionController 9 { 10 public function showUsersAction() 11 { 12 $request = $this->getRequest(); 13 14 // Make sure that we are running in a console and the user has not tricked our 15 // application into running this action from a public web server. 16 if (!$request instanceof ConsoleRequest) { 17 throw new RuntimeException(’You can only use this action from a console!’); 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 use ZendMvcControllerAbstractActionController; 4 use ZendViewModelViewModel; 5 use ZendConsoleRequest as ConsoleRequest; 6 use ZendHttpRequest as HttpRequest; 7 use RuntimeException; 8 9 class IndexController extends AbstractActionController 10 { 11 public function showUsersAction() 12 { 13 $request = $this->getRequest(); 14 15 $users = array(); 16 // ... fetch users from database ... 17 18 if ($request instanceof HttpRequest) { 19 // display a web page with users list 20 return new ViewModel($result); 21 } elseif ($request instanceof ConsoleRequest) { 22 // ... prepare console output and return it ... 23 return $result; 24 } else { 25 throw new RuntimeException(’Cannot handle request of type ’ . get_class($request)); 26 } 27 } 310 Chapter 71. Console-aware action controllers
  • 351. Zend Framework 2 Documentation, Release 2.3.1dev 28 } 71.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. 71.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 // inside of config.console.router.routes: 2 ’show-users’ => array( 3 ’options’ => array( 4 ’route’ => ’show (all|deleted|locked|admin) [<groupName>]’ 5 ’defaults’ => array( 6 ’controller’ => ’ApplicationControllerUsers’, 7 ’action’ => ’showusers’ 8 ) 9 ) 10 ) If this route matches, our action can now query parameters in the following way: 1 // an action inside ApplicationControllerUsersController: 2 public function showUsersAction() 3 { 4 $request = $this->getRequest(); 5 6 // We can access named value parameters directly by their name: 7 $showUsersFromGroup = $request->getParam(’groupName’); 8 9 // Literal parameters can be checked with isset() against their exact spelling 10 if (isset($request->getParam(’all’))) { 11 // show all users 12 } elseif (isset($request->getParam(’deleted’))) { 13 // show deleted users 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: 1 // inside of config.console.router.routes: 2 ’show-users’ => array( 3 ’options’ => array( 4 ’route’ => ’show (all|deleted|locked|admin):userTypeFilter [<groupName>]’ 5 ’defaults’ => array( 6 ’controller’ => ’ApplicationControllerUsers’, 7 ’action’ => ’showusers’ 8 ) 71.4. Reading values from console parameters 311
  • 352. Zend Framework 2 Documentation, Release 2.3.1dev 9 ) 10 ) Now we can use a the group name userTypeFilter to check which option has been selected by the user: 1 public function showUsersAction() 2 { 3 $request = $this->getRequest(); 4 5 // We can access named value parameters directly by their name: 6 $showUsersFromGroup = $request->getParam(’groupName’); 7 8 // The selected option from second parameter is now stored under ’userTypeFilter’ 9 $userTypeFilter = $request->getParam(’userTypeFilter’); 10 11 switch ($userTypeFilter) { 12 case ’all’: 13 // all users 14 case ’deleted’: 15 // deleted users 16 case ’locked’ 17 // ... 18 // ... 19 } 20 } 71.4.2 Flags Flags are directly accessible by name. Value-capturing flags will contain string values, as provided by the user. Non- value flags will be equal to true. Given the following route: 1 ’find-user’ => array( 2 ’options’ => array( 3 ’route’ => ’find user [--fast] [--verbose] [--id=] [--firstName=] [--lastName=] [--email=] 4 ’defaults’ => array( 5 ’controller’ => ’ApplicationControllerUsers’, 6 ’action’ => ’find’, 7 ) 8 ) 9 ) We can easily retrieve values in the following fashion: 1 public function findAction() 2 { 3 $request = $this->getRequest(); 4 5 // We can retrieve values from value flags using their name 6 $searchId = $request->getParam(’id’, null); // default null 7 $searchFirstName = $request->getParam(’firstName’, null); 8 $searchLastName = $request->getParam(’lastName’, null); 9 $searchEmail = $request->getParam(’email’, null); 10 11 // Standard flags that have been matched will be equal to TRUE 12 $isFast = (bool) $request->getParam(’fast’, false); // default false 13 $isVerbose = (bool) $request->getParam(’verbose’,false); 312 Chapter 71. Console-aware action controllers
  • 353. Zend Framework 2 Documentation, Release 2.3.1dev 14 15 if ($isFast) { 16 // perform a fast query ... 17 } else { 18 // perform standard query ... 19 } 20 } In case of flag alternatives, we have to check each alternative separately: 1 // Assuming our route now reads: 2 // ’route’ => ’find user [--fast|-f] [--verbose|-v] ... ’, 3 // 4 public function findAction() 5 { 6 $request = $this->getRequest(); 7 8 // Check both alternatives 9 $isFast = $request->getParam(’fast’,false) || $request->getParam(’f’,false); 10 $isVerbose = $request->getParam(’verbose’,false) || $request->getParam(’v’,false); 11 12 // ... 13 } 71.4. Reading values from console parameters 313
  • 354. Zend Framework 2 Documentation, Release 2.3.1dev 314 Chapter 71. Console-aware action controllers
  • 355. CHAPTER 72 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. 72.1 Retrieving console adapter If you are using MVC controllers you can obtain Console adapter instance using Service Manager. 1 namespace Application; 2 3 use ZendMvcControllerAbstractActionController; 4 use ZendConsoleAdapterAdapterInterface as Console; 5 use ZendConsoleExceptionRuntimeException; 6 7 class ConsoleController extends AbstractActionController 8 { 9 public function testAction() 10 { 11 $console = $this->getServiceLocator()->get(’console’); 12 if (!$console instanceof Console) { 13 throw new RuntimeException(’Cannot obtain console adapter. Are we running in a console?’) 14 } 15 } 16 } If you are using ZendConsole without MVC, we can get adapter using the following code: 1 use ZendConsoleConsole; 2 use ZendConsoleExceptionRuntimeException as ConsoleException; 3 4 try { 5 $console = Console::getInstance(); 6 } catch (ConsoleException $e) { 315
  • 356. Zend Framework 2 Documentation, Release 2.3.1dev 7 // Could not get console adapter - most likely we are not running inside a console window. 8 } 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. 72.2 Using console adapter 72.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 char- acters). 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. 72.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. 72.2.3 Writing to console $console->write( string $text, $color = null, $bgColor = null ) Write a $text to the console, optionally us- ing 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. 316 Chapter 72. Console adapters
  • 357. Zend Framework 2 Documentation, Release 2.3.1dev 72.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. 72.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. 72.2. Using console adapter 317
  • 358. Zend Framework 2 Documentation, Release 2.3.1dev 318 Chapter 72. Console adapters
  • 359. CHAPTER 73 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 $confirm = new PromptConfirm(’Are you sure you want to continue?’); 4 $result = $confirm->show(); 5 if ($result) { 6 // the user chose to continue 7 } There is also a shorter method of displaying prompts, using static prompt() method: 1 use ZendConsolePrompt; 2 3 $result = PromptConfirm::prompt(’Are you sure you want to continue?’); 4 if ($result) { 5 // the user chose to continue 6 } 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. 73.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. 319
  • 360. Zend Framework 2 Documentation, Release 2.3.1dev 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"); } 73.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 ); $console->write("Good day to you $name!"); 73.3 Char This prompt reads a single keystroke and optionally validates it against a list o allowed characters. Char( string $text = ’Please hit a key’, string $allowedChars = ’abc’, bool $ignoreCase = true, bool $allowEmpty = false, bool $echo = true ) $text (string) The text to show with the prompt $allowedChars (string) A list of allowed keys that can be pressed. 320 Chapter 73. Console prompts
  • 361. Zend Framework 2 Documentation, Release 2.3.1dev $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.’); } 73.4 Select This prompt displays a number of choices and asks the user to pick one. Select( string $text = ’Please select one option’, array $options = array(), bool $allowEmpty = false, bool $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 = array( ’a’ => ’Apples’, ’o’ => ’Oranges’, ’p’ => ’Pears’, ’b’ => ’Bananas’, ’n’ => ’none of the above...’ ); $answer = Select::prompt( ’Which fruit do you like the best?’, $options, false, false 73.4. Select 321
  • 362. Zend Framework 2 Documentation, Release 2.3.1dev ); $console->write("You told me that you like " . $options[$answer]); See also: To learn more about accessing console, writing to and reading from it, make sure to read the following chapter: Console adapters. 322 Chapter 73. Console prompts
  • 363. CHAPTER 74 Introduction The ZendConsoleGetopt class helps command-line applications to parse their options and arguments. Users may specify command-line arguments when they execute your application. These arguments have meaning to the application, to change the behavior in some way, or choose resources, or specify parameters. Many options have developed customary meaning, for example --verbose enables extra output from many applications. Other options may have a meaning that is different for each application. For example, -c enables different features in grep, ls, and tar. Below are a few definitions of terms. Common usage of the terms varies, but this documentation will use the definitions below. • “argument”: a string that occurs on the command-line following the name of the command. Arguments may be options or else may appear without an option, to name resources on which the command operates. • “option”: an argument that signifies that the command should change its default behavior in some way. • “flag”: the first part of an option, identifies the purpose of the option. A flag is preceded conventionally by one or two dashes (- or --). A single dash precedes a single-character flag or a cluster of single-character flags. A double-dash precedes a multi-character flag. Long flags cannot be clustered. • “parameter”: the secondary part of an option; a data value that may accompany a flag, if it is applicable to the given option. For example, many commands accept a --verbose option, but typically this option has no parameter. However, an option like --user almost always requires a following parameter. A parameter may be given as a separate argument following a flag argument, or as part of the same argu- ment string, separated from the flag by an equals symbol (=). The latter form is supported only by long flags. For example, -u username, --user username, and --user=username are forms supported by ZendConsoleGetopt. • “cluster”: multiple single-character flags combined in a single string argument and preceded by a single dash. For example, “ls -1str” uses a cluster of four short flags. This command is equivalent to “ls -1 -s -t -r”. Only single-character flags can be clustered. You cannot make a cluster of long flags. For example, in mysql --user=root mydatabase, mysql is a command, --user=root is an option, --user is a flag, root is a parameter to the option, and mydatabase is an argument but not an option by our definition. ZendConsoleGetopt provides an interface to declare which flags are valid for your application, output an error and usage message if they use an invalid flag, and report to your application code which flags the user specified. Note: Getopt is not an Application Framework ZendConsoleGetopt does not interpret the meaning of flags and parameters, nor does this class implement application workflow or invoke application code. You must implement those actions in your own application code. You can use the ZendConsoleGetopt class to parse the command-line and provide object-oriented methods 323
  • 364. Zend Framework 2 Documentation, Release 2.3.1dev for querying which options were given by a user, but code to use this information to invoke parts of your application should be in another PHP class. The following sections describe usage of ZendConsoleGetopt. 324 Chapter 74. Introduction
  • 365. CHAPTER 75 Declaring Getopt Rules The constructor for the ZendConsoleGetopt class takes from one to three arguments. The first argument declares which options are supported by your application. This class supports alternative syntax forms for declaring the options. See the sections below for the format and usage of these syntax forms. The constructor takes two more arguments, both of which are optional. The second argument may contain the command-line arguments. This defaults to $_SERVER[’argv’]. The third argument of the constructor may contain an configuration options to customize the behavior of ZendConsoleGetopt. See Adding Configuration for reference on the options available. 75.1 Declaring Options with the Short Syntax ZendConsoleGetopt supports a compact syntax similar to that used by GNU Getopt (see http://www.gnu.org/software/libc/manual/html_node/Getopt.html. This syntax supports only single-character flags. In a single string, you type each of the letters that correspond to flags supported by your application. A letter followed by a colon character (:) indicates a flag that requires a parameter. Using the Short Syntax 1 $opts = new ZendConsoleGetopt(’abp:’); The example above shows using ZendConsoleGetopt to declare that options may be given as -a, -b, or -p. The latter flag requires a parameter. The short syntax is limited to flags of a single character. Aliases, parameter types, and help strings are not supported in the short syntax. 75.2 Declaring Options with the Long Syntax A different syntax with more features is also available. This syntax allows you to specify aliases for flags, types of option parameters, and also help strings to describe usage to the user. Instead of the single string used in the short syntax to declare the options, the long syntax uses an associative array as the first argument to the constructor. The key of each element of the associative array is a string with a format that names the flag, with any aliases, separated by the pipe symbol (“|”). Following this series of flag aliases, if the option requires a parameter, is an equals symbol (“=”) with a letter that stands for the type of the parameter: • “=s” for a string parameter 325
  • 366. Zend Framework 2 Documentation, Release 2.3.1dev • “=w” for a word parameter (a string containing no whitespace) • “=i” for an integer parameter If the parameter is optional, use a dash (“-”) instead of the equals symbol. The value of each element in the associative array is a help string to describe to a user how to use your program. Using the Long Syntax 1 $opts = new ZendConsoleGetopt( 2 array( 3 ’apple|a’ => ’apple option, with no parameter’, 4 ’banana|b=i’ => ’banana option, with required integer parameter’, 5 ’pear|p-s’ => ’pear option, with optional string parameter’ 6 ) 7 ); In the example declaration above, there are three options. --apple and -a are aliases for each other, and the option takes no parameter. --banana and -b are aliases for each other, and the option takes a mandatory integer parameter. Finally, --pear and -p are aliases for each other, and the option may take an optional string parameter. 326 Chapter 75. Declaring Getopt Rules
  • 367. CHAPTER 76 Fetching Options and Arguments After you have declared the options that the ZendConsoleGetopt object should recognize, and supply argu- ments from the command-line or an array, you can query the object to find out which options were specified by a user in a given command-line invocation of your program. The class implements magic methods so you can query for options by name. The parsing of the data is deferred until the first query you make against the ZendConsoleGetopt object to find out if an option was given, the object performs its parsing. This allows you to use several method calls to configure the options, arguments, help strings, and configuration options before parsing takes place. 76.1 Handling Getopt Exceptions If the user gave any invalid options on the command-line, the parsing function throws a ZendConsoleGetoptException. You should catch this exception in your application code. You can use the parse() method to force the object to parse the arguments. This is useful because you can invoke parse() in a try block. If it passes, you can be sure that the parsing won’t throw an exception again. The exception thrown has a custom method getUsageMessage(), which returns as a string the formatted set of usage messages for all declared options. Catching Getopt Exceptions 1 try { 2 $opts = new ZendConsoleGetopt(’abp:’); 3 $opts->parse(); 4 } catch (ZendConsoleGetoptException $e) { 5 echo $e->getUsageMessage(); 6 exit; 7 } Cases where parsing throws an exception include: • Option given is not recognized. • Option requires a parameter but none was given. • Option parameter is of the wrong type. E.g. a non-numeric string when an integer was required. 327
  • 368. Zend Framework 2 Documentation, Release 2.3.1dev 76.2 Fetching Options by Name You can use the getOption() method to query the value of an option. If the option had a parameter, this method returns the value of the parameter. If the option had no parameter but the user did specify it on the command-line, the method returns TRUE. Otherwise the method returns NULL. Using getOption() 1 $opts = new ZendConsoleGetopt(’abp:’); 2 $b = $opts->getOption(’b’); 3 $p_parameter = $opts->getOption(’p’); Alternatively, you can use the magic __get() function to retrieve the value of an option as if it were a class member variable. The __isset() magic method is also implemented. Using __get() and __isset() Magic Methods 1 $opts = new ZendConsoleGetopt(’abp:’); 2 if (isset($opts->b)) { 3 echo "I got the b option.n"; 4 } 5 $p_parameter = $opts->p; // null if not set If your options are declared with aliases, you may use any of the aliases for an option in the methods above. 76.3 Reporting Options There are several methods to report the full set of options given by the user on the current command-line. • As a string: use the toString() method. The options are returned as a space-separated string of flag=value pairs. The value of an option that does not have a parameter is the literal string “TRUE”. • As an array: use the toArray() method. The options are returned in a simple integer-indexed array of strings, the flag strings followed by parameter strings, if any. • As a string containing JSON data: use the toJson() method. • As a string containing XML data: use the toXml() method. In all of the above dumping methods, the flag string is the first string in the corresponding list of aliases. For example, if the option aliases were declared like verbose|v, then the first string, verbose, is used as the canonical name of the option. The name of the option flag does not include any preceding dashes. 76.4 Fetching Non-option Arguments After option arguments and their parameters have been parsed from the command-line, there may be additional argu- ments remaining. You can query these arguments using the getRemainingArgs() method. This method returns an array of the strings that were not part of any options. 328 Chapter 76. Fetching Options and Arguments
  • 369. Zend Framework 2 Documentation, Release 2.3.1dev Using getRemainingArgs() 1 $opts = new ZendConsoleGetopt(’abp:’); 2 $opts->setArguments(array(’-p’, ’p_parameter’, ’filename’)); 3 $args = $opts->getRemainingArgs(); // returns array(’filename’) ZendConsoleGetopt supports the GNU convention that an argument consisting of a double-dash signifies the end of options. Any arguments following this signifier must be treated as non-option arguments. This is useful if you might have a non-option argument that begins with a dash. For example: “rm -- -filename-with-dash”. 76.4. Fetching Non-option Arguments 329
  • 370. Zend Framework 2 Documentation, Release 2.3.1dev 330 Chapter 76. Fetching Options and Arguments
  • 371. CHAPTER 77 Configuring ZendConsoleGetopt 77.1 Adding Option Rules You can add more option rules in addition to those you specified in the ZendConsoleGetopt constructor, using the addRules() method. The argument to addRules() is the same as the first argument to the class constructor. It is either a string in the format of the short syntax options specification, or else an associative array in the format of a long syntax options specification. See Declaring Getopt Rules for details on the syntax for specifying options. Using addRules() 1 $opts = new ZendConsoleGetopt(’abp:’); 2 $opts->addRules( 3 array( 4 ’verbose|v’ => ’Print verbose output’ 5 ) 6 ); The example above shows adding the --verbose option with an alias of -v to a set of options defined in the call to the constructor. Notice that you can mix short format options and long format options in the same instance of ZendConsoleGetopt. 77.2 Adding Help Messages In addition to specifying the help strings when declaring option rules in the long format, you can associate help strings with option rules using the setHelp() method. The argument to the setHelp() method is an associative array, in which the key is a flag, and the value is a corresponding help string. Using setHelp() 1 $opts = new ZendConsoleGetopt(’abp:’); 2 $opts->setHelp( 3 array( 4 ’a’ => ’apple option, with no parameter’, 5 ’b’ => ’banana option, with required integer parameter’, 6 ’p’ => ’pear option, with optional string parameter’ 7 ) 8 ); 331
  • 372. Zend Framework 2 Documentation, Release 2.3.1dev If you declared options with aliases, you can use any of the aliases as the key of the associative array. The setHelp() method is the only way to define help strings if you declared the options using the short syntax. 77.3 Adding Option Aliases You can declare aliases for options using the setAliases() method. The argument is an associative array, whose key is a flag string declared previously, and whose value is a new alias for that flag. These aliases are merged with any existing aliases. In other words, aliases you declared earlier are still in effect. An alias may be declared only once. If you try to redefine an alias, a ZendConsoleGetoptException is thrown. Using setAliases() 1 $opts = new ZendConsoleGetopt(’abp:’); 2 $opts->setAliases( 3 array( 4 ’a’ => ’apple’, 5 ’a’ => ’apfel’, 6 ’p’ => ’pear’ 7 ) 8 ); In the example above, after declaring these aliases, -a, --apple and --apfel are aliases for each other. Also -p and --pear are aliases for each other. The setAliases() method is the only way to define aliases if you declared the options using the short syntax. 77.4 Adding Argument Lists By default, ZendConsoleGetopt uses $_SERVER[’argv’] for the array of command-line arguments to parse. You can alternatively specify the array of arguments as the second constructor argument. Finally, you can append more arguments to those already used using the addArguments() method, or you can replace the current array of arguments using the setArguments() method. In both cases, the parameter to these methods is a simple array of strings. The former method appends the array to the current arguments, and the latter method substitutes the array for the current arguments. Using addArguments() and setArguments() 1 // By default, the constructor uses $_SERVER[’argv’] 2 $opts = new ZendConsoleGetopt(’abp:’); 3 4 // Append an array to the existing arguments 5 $opts->addArguments(array(’-a’, ’-p’, ’p_parameter’, ’non_option_arg’)); 6 7 // Substitute a new array for the existing arguments 8 $opts->setArguments(array(’-a’, ’-p’, ’p_parameter’, ’non_option_arg’)); 332 Chapter 77. Configuring ZendConsoleGetopt
  • 373. Zend Framework 2 Documentation, Release 2.3.1dev 77.5 Adding Configuration The third parameter to the ZendConsoleGetopt constructor is an array of configuration options that affect the behavior of the object instance returned. You can also specify configuration options using the setOptions() method, or you can set an individual option using the setOption() method. Note: Clarifying the Term “option” The term “option” is used for configuration of the ZendConsoleGetopt class to match terminology used elsewhere in Zend Framework. These are not the same things as the command-line options that are parsed by the ZendConsoleGetopt class. The currently supported options have const definitions in the class. The options, their const identifiers (with literal values in parentheses) are listed below: • ZendConsoleGetopt::CONFIG_DASHDASH (“dashDash”), if TRUE, enables the special flag -- to signify the end of flags. Command-line arguments following the double-dash signifier are not interpreted as options, even if the arguments start with a dash. This configuration option is TRUE by default. • ZendConsoleGetopt::CONFIG_IGNORECASE (“ignoreCase”), if TRUE, makes flags aliases of each other if they differ only in their case. That is, -a and -A will be considered to be synonymous flags. This configuration option is FALSE by default. • ZendConsoleGetopt::CONFIG_RULEMODE (“ruleMode”) may have values ZendConsoleGetopt::MODE_ZEND (“zend”) and ZendConsoleGetopt::MODE_GNU (“gnu”). It should not be necessary to use this option unless you extend the class with additional syntax forms. The two modes supported in the base ZendConsoleGetopt class are unambiguous. If the specifier is a string, the class assumes MODE_GNU, otherwise it assumes MODE_ZEND. But if you extend the class and add more syntax forms, you may need to specify the mode using this option. More configuration options may be added as future enhancements of this class. The two arguments to the setOption() method are a configuration option name and an option value. Using setOption() 1 $opts = new ZendConsoleGetopt(’abp:’); 2 $opts->setOption(’ignoreCase’, true); The argument to the setOptions() method is an associative array. The keys of this array are the configuration option names, and the values are configuration values. This is also the array format used in the class constructor. The configuration values you specify are merged with the current configuration; you don’t have to list all options. Using setOptions() 1 $opts = new ZendConsoleGetopt(’abp:’); 2 $opts->setOptions( 3 array( 4 ’ignoreCase’ => true, 5 ’dashDash’ => false 6 ) 7 ); 77.5. Adding Configuration 333
  • 374. Zend Framework 2 Documentation, Release 2.3.1dev 334 Chapter 77. Configuring ZendConsoleGetopt
  • 375. CHAPTER 78 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. 335
  • 376. Zend Framework 2 Documentation, Release 2.3.1dev 336 Chapter 78. Introduction to ZendCrypt
  • 377. CHAPTER 79 Encrypt/decrypt using block ciphers ZendCryptBlockCipher implements the encrypt-then-authenticate mode using HMAC to provide authentica- tion. The symmetric cipher can be chosen 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 $blockCipher = BlockCipher::factory(’mcrypt’, array(’algo’ => ’aes’)); 4 $blockCipher->setKey(’encryption key’); 5 $result = $blockCipher->encrypt(’this is a secret message’); 6 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 3 $blockCipher = BlockCipher::factory(’mcrypt’, array( 4 ’algo’ => ’blowfish’, 5 ’mode’ => ’cfb’, 337
  • 378. Zend Framework 2 Documentation, Release 2.3.1dev 6 ’hash’ => ’sha512’ 7 )); 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 use ZendCryptBlockCipher; 2 use ZendCryptSymmetricMcrypt; 3 4 $blockCipher = new BlockCipher(new Mcrypt(array(’algo’ => ’aes’))); 5 $blockCipher->setKey(’encryption key’); 6 $result = $blockCipher->encrypt(’this is a secret message’); 7 echo "Encrypted text: $result n"; 338 Chapter 79. Encrypt/decrypt using block ciphers
  • 379. CHAPTER 80 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. 80.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 use ZendCryptKeyDerivationPbkdf2; 2 use ZendMathRand; 3 4 $pass = ’password’; 5 $salt = Rand::getBytes(32, true); 6 $key = Pbkdf2::calc(’sha256’, $pass, $salt, 10000, 32); 7 8 printf ("Original password: %sn", $pass); 9 printf ("Derived key (hex): %sn", bin2hex($key)); The Pbkdf2 adapter takes the password ($pass) and generate a binary key of 32 bytes. The syntax is calc($hash, $pass, $salt, $iterations, $length) where $hash is the name of the hash func- tion 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 the ZendMathRand class to generate a random string of 32 bytes for the salt, using a strong generator (the true value means the usage of a cryptographically strong generator). The number of iterations is a very important parameter for the security of the algorithm. Bigger values guarantee more security. There is not a fixed value for that because the number of iterations depends on the CPU power. You should 339
  • 380. Zend Framework 2 Documentation, Release 2.3.1dev 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. 80.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 to generate a key of 32 bytes. 1 use ZendCryptKeyDerivationSaltedS2k; 2 use ZendMathRand; 3 4 $pass = ’password’; 5 $salt = Rand::getBytes(32, true); 6 $key = SaltedS2k::calc(’sha256’, $pass, $salt, 32); 7 8 printf ("Original password: %sn", $pass); 9 printf ("Derived key (hex): %sn", bin2hex($key)); 80.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 attacker’s 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 32 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. 1 use ZendCryptKeyDerivationScrypt; 2 use ZendMathRand; 3 4 $pass = ’password’; 340 Chapter 80. Key derivation function
  • 381. Zend Framework 2 Documentation, Release 2.3.1dev 5 $salt = Rand::getBytes(32, true); 6 $key = Scrypt::calc($pass, $salt, 2048, 2, 1, 32); 7 8 printf ("Original password: %sn", $pass); 9 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. 80.3. Scrypt adapter 341
  • 382. Zend Framework 2 Documentation, Release 2.3.1dev 342 Chapter 80. Key derivation function
  • 383. CHAPTER 81 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). 81.1 Bcrypt The bcrypt algorithm is an hashing algorithm that is widely used and suggested by the security community to store user’s 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 10, that means about 0.07 second using a CPU Intel i5 at 3.3Ghz (the cost parameter is a relative value according to the speed of the CPU used). We changed the default value of the cost parameter from 14 to 10, starting from Zend Framework 2.3.0, due to high computational time to prevent potential denial-of-service attacks (you can read this article Aggressive password stretching for more information). If you want to change the cost parameter of the bcrypt algorithm you can use the setCost() method. Please note, 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 3 $bcrypt = new Bcrypt(); 4 $securePass = $bcrypt->create(’user password’); 343
  • 384. Zend Framework 2 Documentation, Release 2.3.1dev 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 $bcrypt = new Bcrypt(); 4 $securePass = ’the stored bcrypt value’; 5 $password = ’the password to check’; 6 7 if ($bcrypt->verify($password, $securePass)) { 8 echo "The password is correct! n"; 9 } else { 10 echo "The password is NOT correct.n"; 11 } 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 $bcrypt = new Bcrypt(array( 4 ’salt’ => ’random value’, 5 ’cost’ => 11 6 )); 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 char- acters 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. 81.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; • 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. 344 Chapter 81. Password
  • 385. Zend Framework 2 Documentation, Release 2.3.1dev 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 $apache->setFormat(’crypt’); 6 printf ("CRYPT output: %sn", $apache->create(’password’)); 7 8 $apache->setFormat(’sha1’); 9 printf ("SHA1 output: %sn", $apache->create(’password’)); 10 11 $apache->setFormat(’md5’); 12 printf ("MD5 output: %sn", $apache->create(’password’)); 13 14 $apache->setFormat(’digest’); 15 $apache->setUserName(’enrico’); 16 $apache->setAuthName(’test’); 17 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 $apache = new Apache(array( 4 ’format’ => ’md5’ 5 )); Other possible parameters to pass in the constructor are username and authname, for the digest format. 81.2. Apache 345
  • 386. Zend Framework 2 Documentation, Release 2.3.1dev 346 Chapter 81. Password
  • 387. CHAPTER 82 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. 82.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 Diffie–Hellman 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 Diffie- Hellman 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 $aliceOptions = array( 4 ’prime’ => ’1551728981814736974712322577637155399157248019669154044797077953140576293785419175 5 ’4236981889937278161526466314385615958256881888899512721588426754199503412587065565 6 ’1048705376814767265132557470407658574792912915723345106432450947150072296210941943 7 ’984760375594985848253359305585439638443’, 8 ’generator’=> ’2’, 9 ’private’ => ’9920931406657259523640856959196798855714124956149426748625180803553539633227862014 10 ’81312712891672623072630995180324388841681491857745515696789091127409515009250358965 11 ’46342049838178521379132153348139908016819196219448310107072632515749339055798122538 12 ’04828702523796951800575031871051678091’ 13 ); 14 15 $bobOptions = array( 16 ’prime’ => $aliceOptions[’prime’], 17 ’generator’=> ’2’, 18 ’private’ => ’3341173579263955862573363571789256361254818065040216115107747831484146370794889978 347
  • 388. Zend Framework 2 Documentation, Release 2.3.1dev 19 ’1232563473041055194677275288017786897281696355182174038670007603421340815392469256 20 ’6346473315660054548451083307242700347420706465071483108330449773716038209708335687 21 ’31616972608703322302585471319261275664’ 22 ); 23 24 $alice = new DiffieHellman($aliceOptions[’prime’], $aliceOptions[’generator’], $aliceOptions[’private 25 $bob = new DiffieHellman($bobOptions[’prime’], $bobOptions[’generator’], $bobOptions[’private’]); 26 27 $alice->generateKeys(); 28 $bob->generateKeys(); 29 30 $aliceSecretKey = $alice->computeSecretKey($bob->getPublicKey(DiffieHellman::FORMAT_BINARY), 31 DiffieHellman::FORMAT_BINARY, 32 DiffieHellman::FORMAT_BINARY); 33 34 $bobSecretKey = $bob->computeSecretKey($alice->getPublicKey(DiffieHellman::FORMAT_BINARY), 35 DiffieHellman::FORMAT_BINARY, 36 DiffieHellman::FORMAT_BINARY); 37 38 if ($aliceSecretKey !== $bobSecretKey) { 39 echo "ERROR!n"; 40 } else { 41 printf("The secret key is: %sn", base64_encode($aliceSecretKey)); 42 } 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. 82.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; 348 Chapter 82. Public key cryptography
  • 389. Zend Framework 2 Documentation, Release 2.3.1dev • encrypt/decrypt a string; • generate a digital signature of a file. 82.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 $rsaOptions = new RsaOptions(array( 4 ’pass_phrase’ => ’test’ 5 )); 6 7 $rsaOptions->generateKeys(array( 8 ’private_key_bits’ => 2048, 9 )); 10 11 file_put_contents(’private_key.pem’, $rsaOptions->getPrivateKey()); 12 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 82.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 $rsa = Rsa::factory(array( 4 ’public_key’ => ’public_key.pub’, 5 ’private_key’ => ’private_key.pem’, 6 ’pass_phrase’ => ’test’, 7 ’binary_output’ => false 8 )); 9 10 $text = ’This is the message to encrypt’; 11 12 $encrypt = $rsa->encrypt($text); 13 printf("Encrypted message:n%sn", $encrypt); 14 15 $decrypt = $rsa->decrypt($encrypt); 16 17 if ($text !== $decrypt) { 18 echo "ERRORn"; 19 } else { 82.2. RSA 349
  • 390. Zend Framework 2 Documentation, Release 2.3.1dev 20 echo "Encryption and decryption performed successfully!n"; 21 } 82.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 $rsa = Rsa::factory(array( 4 ’private_key’ => ’path/to/private_key’, 5 ’pass_phrase’ => ’passphrase of the private key’, 6 ’binary_output’ => false 7 )); 8 9 $file = file_get_contents(’path/file/to/sign’); 10 11 $signature = $rsa->sign($file, $rsa->getOptions()->getPrivateKey()); 12 $verify = $rsa->verify($file, $signature, $rsa->getOptions()->getPublicKey()); 13 14 if ($verify) { 15 echo "The signature is OKn"; 16 file_put_contents($filename . ’.sig’, $signature); 17 echo "Signature save in $filename.sign"; 18 } else { 19 echo "The signature is not valid!n"; 20 } 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. 350 Chapter 82. Public key cryptography
  • 391. CHAPTER 83 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. 83.1 Creating an Adapter - Quickstart Creating an adapter can simply be done by instantiating the ZendDbAdapterAdapter class. The most com- mon 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 Is Required? Value driver required Mysqli, Sqlsrv, Pdo_Sqlite, Pdo_Mysql, Pdo=OtherPdoDriver database generally required the name of the database (schema) username generally required the connection username password generally required the connection password hostname not generally required the IP address or hostname to connect to port not generally required the port to connect to (if applicable) charset not generally required 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 $adapter = new ZendDbAdapterAdapter(array( 2 ’driver’ => ’Mysqli’, 3 ’database’ => ’zend_db_example’, 4 ’username’ => ’developer’, 5 ’password’ => ’developer-password’ 6 )); Another example, of a Sqlite connection via PDO: 351
  • 392. Zend Framework 2 Documentation, Release 2.3.1dev 1 $adapter = new ZendDbAdapterAdapter(array( 2 ’driver’ => ’Pdo_Sqlite’, 3 ’database’ => ’path/to/sqlite.db’ 4 )); It is important to know that by using this style of adapter creation, the Adapter will attempt to create any depen- dencies 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 83.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 use ZendDbAdapterPlatformPlatformInterface; 2 use ZendDbResultSetResultSet; 3 4 class ZendDbAdapterAdapter { 5 public function __construct($driver, PlatformInterface $platform = null, ResultSet $queryResultSe 6 } What can be injected: • $driver - an array of connection parameters (see above) or an instance of ZendDbAdapterDriverDriverInterface • $platform - (optional) an instance of ZendDbPlatformPlatformInterface, the default will be cre- ated 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 83.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: 1 $adapter->query(’SELECT * FROM ‘artist‘ WHERE ‘id‘ = ?’, array(5)); The above example will go through the following steps: 352 Chapter 83. ZendDbAdapter
  • 393. Zend Framework 2 Documentation, Release 2.3.1dev • 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 83.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. 83.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 // with optional parameters to bind up-front 2 $statement = $adapter->createStatement($sql, $optionalParameters); 3 $result = $statement->execute(); 83.6 Using the Driver Object The Driver object is the primary place where ZendDbAdapterAdapter implements the connection level ab- straction 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 • The adapter chooses the proper driver to instantiate, for example ZendDbAdapterDriverMysqli • That driver class is instantiated 83.4. Query Execution Through ZendDbAdapterAdapter::query() 353
  • 394. Zend Framework 2 Documentation, Release 2.3.1dev • 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 3 interface DriverInterface 4 { 5 const PARAMETERIZATION_POSITIONAL = ’positional’; 6 const PARAMETERIZATION_NAMED = ’named’; 7 const NAME_FORMAT_CAMELCASE = ’camelCase’; 8 const NAME_FORMAT_NATURAL = ’natural’; 9 public function getDatabasePlatformName($nameFormat = self::NAME_FORMAT_CAMELCASE); 10 public function checkEnvironment(); 11 public function getConnection(); 12 public function createStatement($sqlOrResource = null); 13 public function createResult($resource); 14 public function getPrepareType(); 15 public function formatParameterName($name, $type = null); 16 public function getLastGeneratedValue(); 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 interface StatementInterface extends StatementContainerInterface 4 { 5 public function getResource(); 6 public function prepare($sql = null); 7 public function isPrepared(); 8 public function execute($parameters = null); 9 10 /** Inherited from StatementContainerInterface */ 11 public function setSql($sql); 12 public function getSql(); 13 public function setParameterContainer(ParameterContainer $parameterContainer); 14 public function getParameterContainer(); 15 } Result objects generally look like this: 354 Chapter 83. ZendDbAdapter
  • 395. Zend Framework 2 Documentation, Release 2.3.1dev 1 namespace ZendDbAdapterDriver; 2 3 interface ResultInterface extends Countable, Iterator 4 { 5 public function buffer(); 6 public function isQueryResult(); 7 public function getAffectedRows(); 8 public function getGeneratedValue(); 9 public function getResource(); 10 public function getFieldCount(); 11 } 83.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 interface PlatformInterface 4 { 5 public function getName(); 6 public function getQuoteIdentifierSymbol(); 7 public function quoteIdentifier($identifier); 8 public function quoteIdentifierChain($identiferChain) 9 public function getQuoteValueSymbol(); 10 public function quoteValue($value); 11 public function quoteValueList($valueList); 12 public function getIdentifierSeparator(); 13 public function quoteIdentifierInFragment($identifier, array $additionalSafeWords = array()); 14 } 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 $platform = $adapter->getPlatform(); 2 // or 3 $platform = $adapter->platform; // magic property access The following is a couple of example of Platform usage: 1 /** @var $adapter ZendDbAdapterAdapter */ 2 /** @var $platform ZendDbAdapterPlatformSql92 */ 3 $platform = $adapter->getPlatform(); 4 5 // "first_name" 6 echo $platform->quoteIdentifier(’first_name’); 7 8 // " 9 echo $platform->getQuoteIdentifierSymbol(); 10 11 // "schema"."mytable" 12 echo $platform->quoteIdentifierChain(array(’schema’,’mytable’))); 13 14 // ’ 83.7. Using The Platform Object 355
  • 396. Zend Framework 2 Documentation, Release 2.3.1dev 15 echo $platform->getQuoteValueSymbol(); 16 17 // ’myvalue’ 18 echo $platform->quoteValue(’myvalue’); 19 20 // ’value’, ’Foo O’Bar’ 21 echo $platform->quoteValueList(array(’value’,"Foo O’Bar"))); 22 23 // . 24 echo $platform->getIdentifierSeparator(); 25 26 // "foo" as "bar" 27 echo $platform->quoteIdentifierInFragment(’foo as bar’); 28 29 // additionally, with some safe words: 30 // ("foo"."bar" = "boo"."baz") 31 echo $platform->quoteIdentifierInFragment(’(foo.bar = boo.baz)’, array(’(’, ’)’, ’=’)); 83.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: */ public function count() 356 Chapter 83. ZendDbAdapter
  • 397. Zend Framework 2 Documentation, Release 2.3.1dev public function current() public function next() public function key() public function valid() public function 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 param- eter: $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. 83.9 Examples Creating a Driver and Vendor portable Query, Preparing and Iterating Result 1 $adapter = new ZendDbAdapterAdapter($driverConfig); 2 3 $qi = function($name) use ($adapter) { return $adapter->platform->quoteIdentifier($name); }; 4 $fp = function($name) use ($adapter) { return $adapter->driver->formatParameterName($name); }; 5 6 $sql = ’UPDATE ’ . $qi(’artist’) 7 . ’ SET ’ . $qi(’name’) . ’ = ’ . $fp(’name’) 8 . ’ WHERE ’ . $qi(’id’) . ’ = ’ . $fp(’id’); 9 10 /** @var $statement ZendDbAdapterDriverStatementInterface */ 11 $statement = $adapter->query($sql); 12 13 $parameters = array( 14 ’name’ => ’Updated Artist’, 15 ’id’ => 1 16 ); 17 18 $statement->execute($parameters); 19 20 // DATA INSERTED, NOW CHECK 21 22 /* @var $statement ZendDbAdapterDriverStatementInterface */ 23 $statement = $adapter->query(’SELECT * FROM ’ 24 . $qi(’artist’) 25 . ’ WHERE id = ’ . $fp(’id’)); 26 27 /* @var $results ZendDbResultSetResultSet */ 28 $results = $statement->execute(array(’id’ => 1)); 29 83.9. Examples 357
  • 398. Zend Framework 2 Documentation, Release 2.3.1dev 30 $row = $results->current(); 31 $name = $row[’name’]; 358 Chapter 83. ZendDbAdapter
  • 399. CHAPTER 84 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 sub- components 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 interface ResultSetInterface extends Traversable, Countable 2 { 3 public function initialize($dataSource); 4 public function getFieldCount(); 5 } 84.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 similar to what one might find inside ZendDbAdapterAdapter::query(): 1 use ZendDbAdapterDriverResultInterface; 2 use ZendDbResultSetResultSet; 3 4 $stmt = $driver->createStatement(’SELECT * FROM users’); 5 $stmt->prepare(); 6 $result = $stmt->execute($parameters); 7 8 if ($result instanceof ResultInterface && $result->isQueryResult()) { 9 $resultSet = new ResultSet; 10 $resultSet->initialize($result); 11 12 foreach ($resultSet as $row) { 13 echo $row->my_column . PHP_EOL; 14 } 15 } 359
  • 400. Zend Framework 2 Documentation, Release 2.3.1dev 84.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 abstract class AbstractResultSet implements Iterator, ResultSetInterface 2 { 3 public function initialize($dataSource) 4 public function getDataSource() 5 public function getFieldCount() 6 7 /** Iterator */ 8 public function next() 9 public function key() 10 public function current() 11 public function valid() 12 public function rewind() 13 14 /** countable */ 15 public function count() 16 17 /** get rows as array */ 18 public function toArray() 19 } 84.3 ZendDbResultSetHydratingResultSet ZendDbResultSetHydratingResultSet is a more flexible ResultSet object that allows the devel- oper 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 use ZendDbAdapterDriverResultInterface; 2 use ZendDbResultSetHydratingResultSet; 3 use ZendStdlibHydratorReflection as ReflectionHydrator; 4 5 class UserEntity { 6 protected $first_name; 7 protected $last_name; 8 public function getFirstName() { return $this->first_name; } 9 public function getLastName() { return $this->last_name; } 10 public function setFirstName($first_name) { $this->first_name = $first_name; } 11 public function setLastName($last_name) { $this->last_name = $last_name; } 12 } 13 14 $stmt = $driver->createStatement($sql); 15 $stmt->prepare($parameters); 16 $result = $stmt->execute(); 17 18 if ($result instanceof ResultInterface && $result->isQueryResult()) { 19 $resultSet = new HydratingResultSet(new ReflectionHydrator, new UserEntity); 20 $resultSet->initialize($result); 360 Chapter 84. ZendDbResultSet
  • 401. Zend Framework 2 Documentation, Release 2.3.1dev 21 22 foreach ($resultSet as $user) { 23 echo $user->getFirstName() . ’ ’ . $user->getLastName() . PHP_EOL; 24 } 25 } For more information, see the ZendStdlibHydrator documentation to get a better sense of the different strate- gies that can be employed in order to populate a target object. 84.3. ZendDbResultSetHydratingResultSet 361
  • 402. Zend Framework 2 Documentation, Release 2.3.1dev 362 Chapter 84. ZendDbResultSet
  • 403. CHAPTER 85 ZendDbSql ZendDbSql is a SQL abstraction layer for building platform specific SQL queries via an object-oriented API. The end result of an ZendDbSql object will be to either produce a Statement and Parameter container that rep- resents 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. 85.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 use ZendDbSqlSql; 2 $sql = new Sql($adapter); 3 $select = $sql->select(); // @return ZendDbSqlSelect 4 $insert = $sql->insert(); // @return ZendDbSqlInsert 5 $update = $sql->update(); // @return ZendDbSqlUpdate 6 $delete = $sql->delete(); // @return 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 use ZendDbSqlSql; 2 $sql = new Sql($adapter); 3 $select = $sql->select(); 4 $select->from(’foo’); 5 $select->where(array(’id’ => 2)); 6 7 $statement = $sql->prepareStatementForSqlObject($select); 8 $results = $statement->execute(); To execute (using a Select object) 1 use ZendDbSqlSql; 2 $sql = new Sql($adapter); 3 $select = $sql->select(); 4 $select->from(’foo’); 363
  • 404. Zend Framework 2 Documentation, Release 2.3.1dev 5 $select->where(array(’id’ => 2)); 6 7 $selectString = $sql->getSqlStringForSqlObject($select); 8 $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 use ZendDbSqlSql; 2 $sql = new Sql($adapter, ’foo’); 3 $select = $sql->select(); 4 $select->where(array(’id’ => 2)); // $select already has the from(’foo’) applied 85.2 ZendDbSql’s Select, Insert, Update and Delete Each of these objects implements the following (2) interfaces: 1 interface PreparableSqlInterface { 2 public function prepareStatement(Adapter $adapter, StatementInterface $statement); 3 } 4 interface SqlInterface { 5 public function getSqlString(PlatformInterface $adapterPlatform = null); 6 } These are the functions you can call to either produce (a) a prepared statement, or (b) a string to be executed. 85.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 use ZendDbSqlSelect; 2 $select = new Select(); 3 // or, to produce a $select bound to a specific table 4 $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 class Select extends AbstractSql implements SqlInterface, PreparableSqlInterface 2 { 3 const JOIN_INNER = ’inner’; 4 const JOIN_OUTER = ’outer’; 5 const JOIN_LEFT = ’left’; 6 const JOIN_RIGHT = ’right’; 7 const SQL_STAR = ’*’; 8 const ORDER_ASCENDING = ’ASC’; 9 const ORDER_DESCENDING = ’DESC’; 10 11 public $where; // @param Where $where 12 13 public function __construct($table = null); 14 public function from($table); 15 public function columns(array $columns, $prefixColumnsWithTable = true); 364 Chapter 85. ZendDbSql
  • 405. Zend Framework 2 Documentation, Release 2.3.1dev 16 public function join($name, $on, $columns = self::SQL_STAR, $type = self::JOIN_INNER); 17 public function where($predicate, $combination = PredicatePredicateSet::OP_AND); 18 public function group($group); 19 public function having($predicate, $combination = PredicatePredicateSet::OP_AND); 20 public function order($order); 21 public function limit($limit); 22 public function offset($offset); 23 } 85.3.1 from(): 1 // as a string: 2 $select->from(’foo’); 3 4 // as an array to specify an alias: 5 // produces SELECT "t".* FROM "table" AS "t" 6 7 $select->from(array(’t’ => ’table’)); 8 9 // using a SqlTableIdentifier: 10 // same output as above 11 12 $select->from(new TableIdentifier(array(’t’ => ’table’))); 85.3.2 columns(): 1 // as array of names 2 $select->columns(array(’foo’, ’bar’)); 3 4 // as an associative array with aliases as the keys: 5 // produces ’bar’ AS ’foo’, ’bax’ AS ’baz’ 6 7 $select->columns(array(’foo’ => ’bar’, ’baz’ => ’bax’)); 85.3.3 join(): 1 $select->join( 2 ’foo’, // table name 3 ’id = bar.id’, // expression to join on (will be quoted by platform object before insertion), 4 array(’bar’, ’baz’), // (optional) list of columns, same requirements as columns() above 5 $select::JOIN_OUTER // (optional), one of inner, outer, left, right also represented by constant 6 ); 7 8 $select->from(array(’f’ => ’foo’)) // base table 9 ->join(array(’b’ => ’bar’), // join table with alias 10 ’f.foo_id = b.foo_id’); // join expression 85.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: 85.3. ZendDbSqlSelect 365
  • 406. Zend Framework 2 Documentation, Release 2.3.1dev 1 /** 2 * Create where clause 3 * 4 * @param Where|Closure|string|array $predicate 5 * @param string $combination One of the OP_* constants from PredicatePredicateSet 6 * @return Select 7 */ 8 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 $spec = function (Where $where) { 2 $where->like(’username’, ’ralph%’); 3 }; 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 // SELECT "foo".* FROM "foo" WHERE x = 5 2 $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 // SELECT "foo".* FROM "foo" WHERE x = 5 AND y = z 2 $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 // SELECT "foo".* FROM "foo" WHERE "c1" IS NULL AND "c2" IN (?, ?, ?) AND "c3" IS NOT NULL 2 $select->from(’foo’)->where(array( 3 ’c1’ => null, 4 ’c2’ => array(1, 2, 3), 5 new ZendDbSqlPredicateIsNotNull(’c3’) 6 )); 366 Chapter 85. ZendDbSql
  • 407. Zend Framework 2 Documentation, Release 2.3.1dev 85.3.5 order(): 1 $select = new Select; 2 $select->order(’id DESC’); // produces ’id’ DESC 3 4 $select = new Select; 5 $select->order(’id DESC’) 6 ->order(’name ASC, age DESC’); // produces ’id’ DESC, ’name’ ASC, ’age’ DESC 7 8 $select = new Select; 9 $select->order(array(’name ASC’, ’age DESC’)); // produces ’name’ ASC, ’age’ DESC 85.3.6 limit() and offset(): 1 $select = new Select; 2 $select->limit(5); // always takes an integer/numeric 3 $select->offset(10); // similarly takes an integer/numeric 85.4 ZendDbSqlInsert The Insert API: 1 class Insert implements SqlInterface, PreparableSqlInterface 2 { 3 const VALUES_MERGE = ’merge’; 4 const VALUES_SET = ’set’; 5 6 public function __construct($table = null); 7 public function into($table); 8 public function columns(array $columns); 9 public function values(array $values, $flag = self::VALUES_SET); 10 } Similarly to Select objects, the table can be set at construction time or via into(). 85.4.1 columns(): 1 $insert->columns(array(’foo’, ’bar’)); // set the valid columns 85.4.2 values(): 1 // default behavior of values is to set the values 2 // successive calls will not preserve values from previous calls 3 $insert->values(array( 4 ’col_1’ => ’value1’, 5 ’col_2’ => ’value2’ 6 )); 1 // merging values with previous calls 2 $insert->values(array(’col_2’ => ’value2’), $insert::VALUES_MERGE); 85.4. ZendDbSqlInsert 367
  • 408. Zend Framework 2 Documentation, Release 2.3.1dev 85.5 ZendDbSqlUpdate 1 class Update 2 { 3 const VALUES_MERGE = ’merge’; 4 const VALUES_SET = ’set’; 5 6 public $where; // @param Where $where 7 public function __construct($table = null); 8 public function table($table); 9 public function set(array $values, $flag = self::VALUES_SET); 10 public function where($predicate, $combination = PredicatePredicateSet::OP_AND); 11 } 85.5.1 set(): 1 $update->set(array(’foo’ => ’bar’, ’baz’ => ’bax’)); 85.5.2 where(): See where section below. 85.6 ZendDbSqlDelete 1 class Delete 2 { 3 public $where; // @param Where $where 4 public function __construct($table = null); 5 public function from($table); 6 public function where($predicate, $combination = PredicatePredicateSet::OP_AND); 7 } 85.6.1 where(): See where section below. 85.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 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. 368 Chapter 85. ZendDbSql
  • 409. Zend Framework 2 Documentation, Release 2.3.1dev 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 // Where & Having: 2 class Predicate extends PredicateSet 3 { 4 public $and; 5 public $or; 6 public $AND; 7 public $OR; 8 public $NEST; 9 public $UNNEST; 10 11 public function nest(); 12 public function setUnnest(Predicate $predicate); 13 public function unnest(); 14 public function equalTo($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightType = self::TYP 15 public function lessThan($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightType = self::TY 16 public function greaterThan($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightType = self: 17 public function lessThanOrEqualTo($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightType = 18 public function greaterThanOrEqualTo($left, $right, $leftType = self::TYPE_IDENTIFIER, $rightTyp 19 public function like($identifier, $like); 20 public function literal($literal); 21 public function expression($expression, $parameter); 22 public function isNull($identifier); 23 public function isNotNull($identifier); 24 public function in($identifier, array $valueSet = array()); 25 public function between($identifier, $minValue, $maxValue); 26 27 28 // Inherited From PredicateSet 29 30 public function addPredicate(PredicateInterface $predicate, $combination = null); 31 public function getPredicates(); 32 public function orPredicate(PredicateInterface $predicate); 33 public function andPredicate(PredicateInterface $predicate); 34 public function getExpressionData(); 35 public function count(); 36 } 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: 85.7. ZendDbSqlWhere & ZendDbSqlHaving 369
  • 410. Zend Framework 2 Documentation, Release 2.3.1dev 85.7.1 equalTo(), lessThan(), greaterThan(), lessThanOrEqualTo(), greaterThanOrE- qualTo(): 1 $where->equalTo(’id’, 5); 2 3 // same as the following workflow 4 $where->addPredicate( 5 new PredicateOperator($left, Operator::OPERATOR_EQUAL_TO, $right, $leftType, $rightType) 6 ); 7 8 class Operator implements PredicateInterface 9 { 10 const OPERATOR_EQUAL_TO = ’=’; 11 const OP_EQ = ’=’; 12 const OPERATOR_NOT_EQUAL_TO = ’!=’; 13 const OP_NE = ’!=’; 14 const OPERATOR_LESS_THAN = ’<’; 15 const OP_LT = ’<’; 16 const OPERATOR_LESS_THAN_OR_EQUAL_TO = ’<=’; 17 const OP_LTE = ’<=’; 18 const OPERATOR_GREATER_THAN = ’>’; 19 const OP_GT = ’>’; 20 const OPERATOR_GREATER_THAN_OR_EQUAL_TO = ’>=’; 21 const OP_GTE = ’>=’; 22 23 public function __construct($left = null, $operator = self::OPERATOR_EQUAL_TO, $right = null, $le 24 public function setLeft($left); 25 public function getLeft(); 26 public function setLeftType($type); 27 public function getLeftType(); 28 public function setOperator($operator); 29 public function getOperator(); 30 public function setRight($value); 31 public function getRight(); 32 public function setRightType($type); 33 public function getRightType(); 34 public function getExpressionData(); 35 } 85.7.2 like($identifier, $like): 1 $where->like($identifier, $like): 2 3 // same as 4 $where->addPredicate( 5 new PredicateLike($identifier, $like) 6 ); 7 8 // full API 9 10 class Like implements PredicateInterface 11 { 12 public function __construct($identifier = null, $like = null); 13 public function setIdentifier($identifier); 14 public function getIdentifier(); 15 public function setLike($like); 370 Chapter 85. ZendDbSql
  • 411. Zend Framework 2 Documentation, Release 2.3.1dev 16 public function getLike(); 17 } 85.7.3 literal($literal); 1 $where->literal($literal); 2 3 // same as 4 $where->addPredicate( 5 new PredicateLiteral($literal) 6 ); 7 8 // full API 9 class Literal implements ExpressionInterface, PredicateInterface 10 { 11 const PLACEHOLDER = ’?’; 12 public function __construct($literal = ’’); 13 public function setLiteral($literal); 14 public function getLiteral(); 15 } 85.7.4 expression($expression, $parameter); 1 $where->expression($expression, $parameter); 2 3 // same as 4 $where->addPredicate( 5 new PredicateExpression($expression, $parameter) 6 ); 7 8 // full API 9 class Expression implements ExpressionInterface, PredicateInterface 10 { 11 const PLACEHOLDER = ’?’; 12 public function __construct($expression = null, $valueParameter = null /*[, $valueParameter, ... 13 public function setExpression($expression); 14 public function getExpression(); 15 public function setParameters($parameters); 16 public function getParameters(); 17 public function setTypes(array $types); 18 public function getTypes(); 19 } 85.7.5 isNull($identifier); 1 $where->isNull($identifier); 2 3 // same as 4 $where->addPredicate( 5 new PredicateIsNull($identifier) 6 ); 7 8 // full API 85.7. ZendDbSqlWhere & ZendDbSqlHaving 371
  • 412. Zend Framework 2 Documentation, Release 2.3.1dev 9 class IsNull implements PredicateInterface 10 { 11 public function __construct($identifier = null); 12 public function setIdentifier($identifier); 13 public function getIdentifier(); 14 } 85.7.6 isNotNull($identifier); 1 $where->isNotNull($identifier); 2 3 // same as 4 $where->addPredicate( 5 new PredicateIsNotNull($identifier) 6 ); 7 8 // full API 9 class IsNotNull implements PredicateInterface 10 { 11 public function __construct($identifier = null); 12 public function setIdentifier($identifier); 13 public function getIdentifier(); 14 } 85.7.7 in($identifier, array $valueSet = array()); 1 $where->in($identifier, array $valueSet = array()); 2 3 // same as 4 $where->addPredicate( 5 new PredicateIn($identifier, $valueSet) 6 ); 7 8 // full API 9 class In implements PredicateInterface 10 { 11 public function __construct($identifier = null, array $valueSet = array()); 12 public function setIdentifier($identifier); 13 public function getIdentifier(); 14 public function setValueSet(array $valueSet); 15 public function getValueSet(); 16 } 85.7.8 between($identifier, $minValue, $maxValue); 1 $where->between($identifier, $minValue, $maxValue); 2 3 // same as 4 $where->addPredicate( 5 new PredicateBetween($identifier, $minValue, $maxValue) 6 ); 7 8 // full API 372 Chapter 85. ZendDbSql
  • 413. Zend Framework 2 Documentation, Release 2.3.1dev 9 class Between implements PredicateInterface 10 { 11 public function __construct($identifier = null, $minValue = null, $maxValue = null); 12 public function setIdentifier($identifier); 13 public function getIdentifier(); 14 public function setMinValue($minValue); 15 public function getMinValue(); 16 public function setMaxValue($maxValue); 17 public function getMaxValue(); 18 public function setSpecification($specification); 19 } 85.7. ZendDbSqlWhere & ZendDbSqlHaving 373
  • 414. Zend Framework 2 Documentation, Release 2.3.1dev 374 Chapter 85. ZendDbSql
  • 415. CHAPTER 86 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 state- ments, 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 86.1 Creating Tables Like ZendDbSql objects, each statement type is represented by a class. For example, CREATE TABLE is mod- eled 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 // or with table 6 $table = new DdlCreateTable(’bar’); 7 8 // optionally, as a temporary table 9 $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 use ZendDbSqlDdlColumn; 2 $table->addColumn(new ColumnInteger(’id’)); 3 $table->addColumn(new ColumnVarchar(’name’, 255)); Beyond adding columns to a table, constraints can also be added: 375
  • 416. Zend Framework 2 Documentation, Release 2.3.1dev 1 use ZendDbSqlDdlConstraint; 2 $table->addConstraint(new ConstraintPrimaryKey(’id’)); 3 $table->addConstraint( 4 new ConstraintUniqueKey([’name’, ’foo’], ’my_unique_key’) 5 ); 86.2 Altering Tables Similarly to CreateTable, you may also instantiate AlterTable: 1 use ZendDbSqlDdl; 2 3 $table = new DdlAlterTable(); 4 5 // or with table 6 $table = new DdlAlterTable(’bar’); 7 8 // optionally, as a temporary table 9 $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 use ZendDbSqlDdlColumn; 2 $table->changeColumn(’name’, ColumnVarchar(’new_name’, 50)); You may also drop existing columns or constraints: 1 $table->dropColumn(’foo’); 2 $table->dropConstraint(’my_index’); 86.3 Dropping Tables To drop a table, create a DropTable statement object: 1 $drop = new DdlDropTable(’bar’); 86.4 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 in- stance: 1 use ZendDbSqlSql; 2 3 // existence of $adapter is assumed 4 $sql = new Sql($adapter); 5 6 $adapter->query( 376 Chapter 86. ZendDbSqlDdl
  • 417. Zend Framework 2 Documentation, Release 2.3.1dev 7 $sql->getSqlStringForSqlObject($ddl), 8 $adapter::QUERY_MODE_EXECUTE 9 ); 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 state- ment is not prepared, as many DDL statements on a variety of platforms cannot be prepared, only executed. 86.5 Currently Supported Data Types These types exist in the ZendDbSqlDdlColumn namespace. Data types must implement ZendDbSqlDdlColumnColumnInterface. In alphabetical order: Type Arguments For Construction Blob $name, $length, $nullable = false, $default = null, array $options = array() Boolean $name Char $name, $length Column (generic) $name = null Date $name Decimal $name, $precision, $scale = null Float $name, $digits, $decimal Integer $name, $nullable = false, $default = null, array $options = array() Time $name Varchar $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. 86.6 Currently Supported Constraint Types These types exist in the ZendDbSqlDdlConstraint namespace. Data types must implement ZendDbSqlDdlConstraintConstraintInterface. In alphabetical order: Type Arguments For Construction Check $expression, $name For- eignKey $name, $column, $referenceTable, $referenceColumn, $onDeleteRule = null, $onUpdateRule = null Prima- ryKey $columns 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(). 86.5. Currently Supported Data Types 377
  • 418. Zend Framework 2 Documentation, Release 2.3.1dev 378 Chapter 86. ZendDbSqlDdl
  • 419. CHAPTER 87 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 interface ZendDbTableGatewayTableGatewayInterface 2 { 3 public function getTable(); 4 public function select($where = null); 5 public function insert($set); 6 public function update($set, $where = null); 7 public function delete($where); 8 } There are two primary implementations of the TableGatewayInterface that are of the most useful: AbstractTableGateway and TableGateway. The AbstractTableGateway is an abstract basic imple- mentation that provides functionality for select(), insert(), update(), delete(), as well as an addi- tional 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. 87.1 Basic Usage The quickest way to get up and running with ZendDbTableGateway is to configure and utilize the concrete imple- mentation of the TableGateway. The API of the concrete TableGateway is: 1 class TableGateway extends AbstractTableGateway 2 { 3 public $lastInsertValue; 4 public $table; 5 public $adapter; 6 7 public function __construct($table, Adapter $adapter, $features = null, ResultSet $resultSetProt 8 9 /** Inherited from AbstractTableGateway */ 10 11 public function isInitialized(); 12 public function initialize(); 379
  • 420. Zend Framework 2 Documentation, Release 2.3.1dev 13 public function getTable(); 14 public function getAdapter(); 15 public function getColumns(); 16 public function getFeatureSet(); 17 public function getResultSetPrototype(); 18 public function getSql(); 19 public function select($where = null); 20 public function selectWith(Select $select); 21 public function insert($set); 22 public function insertWith(Insert $insert); 23 public function update($set, $where = null); 24 public function updateWith(Update $update); 25 public function delete($where); 26 public function deleteWith(Delete $delete); 27 public function getLastInsertValue(); 28 } 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 use ZendDbTableGatewayTableGateway; 2 $projectTable = new TableGateway(’project’, $adapter); 3 $rowset = $projectTable->select(array(’type’ => ’PHP’)); 4 5 echo ’Projects of type PHP: ’; 6 foreach ($rowset as $projectRow) { 7 echo $projectRow[’name’] . PHP_EOL; 8 } 9 10 // or, when expecting a single row: 11 $artistTable = new TableGateway(’artist’, $adapter); 12 $rowset = $artistTable->select(array(’id’ => 2)); 13 $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 use ZendDbTableGatewayTableGateway; 2 use ZendDbSqlSelect; 3 $artistTable = new TableGateway(’artist’, $adapter); 4 5 // search for at most 2 artists who’s name starts with Brit, ascending 6 $rowset = $artistTable->select(function (Select $select) { 7 $select->where->like(’name’, ’Brit%’); 8 $select->order(’name ASC’)->limit(2); 9 }); 380 Chapter 87. ZendDbTableGateway
  • 421. Zend Framework 2 Documentation, Release 2.3.1dev 87.2 TableGateway Features The Features API allows for extending the functionality of the base TableGateway object without having to poly- morphically 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 Fea- tures 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 im- plementation: 1 use ZendDbTableGatewayAbstractTableGateway; 2 use ZendDbTableGatewayFeature; 3 4 class MyTableGateway extends AbstractTableGateway 5 { 6 public function __construct() 7 { 8 $this->table = ’my_table’; 9 $this->featureSet = new FeatureFeatureSet(); 10 $this->featureSet->addFeature(new FeatureGlobalAdapterFeature()); 11 $this->initialize(); 12 } 13 } 14 15 // elsewhere in code, in a bootstrap 16 ZendDbTableGatewayFeatureGlobalAdapterFeature::setStaticAdapter($adapter); 17 18 // in a controller, or model somewhere 19 $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 $table = new TableGateway(’artist’, $adapter, new FeatureRowGatewayFeature(’id’)); 2 $results = $table->select(array(’id’ => 2)); 3 4 $artistRow = $results->current(); 87.2. TableGateway Features 381
  • 422. Zend Framework 2 Documentation, Release 2.3.1dev 5 $artistRow->name = ’New Name’; 6 $artistRow->save(); 382 Chapter 87. ZendDbTableGateway
  • 423. CHAPTER 88 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 interface RowGatewayInterface 2 { 3 public function save(); 4 public function delete(); 5 } 88.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 // query the database 4 $resultSet = $adapter->query(’SELECT * FROM ‘user‘ WHERE ‘id‘ = ?’, array(2)); 5 6 // get array of data 7 $rowData = $resultSet->current()->getArrayCopy(); 8 9 // row gateway 10 $rowGateway = new RowGateway(’id’, ’my_table’, $adapter); 11 $rowGateway->populate($rowData); 12 13 $rowGateway->first_name = ’New Name’; 14 $rowGateway->save(); 15 16 // or delete this row: 17 $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 383
  • 424. Zend Framework 2 Documentation, Release 2.3.1dev that is then capable of producing valid Row Gateway objects. Its usage looks like this: 1 use ZendDbTableGatewayFeatureRowGatewayFeature; 2 use ZendDbTableGatewayTableGateway; 3 4 $table = new TableGateway(’artist’, $adapter, new RowGatewayFeature(’id’)); 5 $results = $table->select(array(’id’ => 2)); 6 7 $artistRow = $results->current(); 8 $artistRow->name = ’New Name’; 9 $artistRow->save(); 88.2 ActiveRecord Style Objects If you wish to have custom behaviour for your RowGateway objects (essentially making them behave simi- larly to the ActiveRecord pattern), pass a prototype object implementing the RowGatewayInterface to the RowGatewayFeature constructor instead of a primary key: 1 use ZendDbTableGatewayFeatureRowGatewayFeature; 2 use ZendDbTableGatewayTableGateway; 3 use ZendDbRowGatewayRowGatewayInterface; 4 5 class Artist implements RowGatewayInterface 6 { 7 protected $adapter; 8 9 public function __construct($adapter) 10 { 11 $this->adapter = $adapter; 12 } 13 14 // ... save() and delete() implementations 15 } 16 17 $table = new TableGateway(’artist’, $adapter, new RowGatewayFeature(new Artist($adapter))); 384 Chapter 88. ZendDbRowGateway
  • 425. CHAPTER 89 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 interface MetadataInterface 2 { 3 public function getSchemas(); 4 5 public function getTableNames($schema = null, $includeViews = false); 6 public function getTables($schema = null, $includeViews = false); 7 public function getTable($tableName, $schema = null); 8 9 public function getViewNames($schema = null); 10 public function getViews($schema = null); 11 public function getView($viewName, $schema = null); 12 13 public function getColumnNames($table, $schema = null); 14 public function getColumns($table, $schema = null); 15 public function getColumn($columnName, $table, $schema = null); 16 17 public function getConstraints($table, $schema = null); 18 public function getConstraint($constraintName, $table, $schema = null); 19 public function getConstraintKeys($constraint, $table, $schema = null); 20 21 public function getTriggerNames($schema = null); 22 public function getTriggers($schema = null); 23 public function getTrigger($triggerName, $schema = null); 24 } 89.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. 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. 385
  • 426. Zend Framework 2 Documentation, Release 2.3.1dev 1 $metadata = new ZendDbMetadataMetadata($adapter); 2 3 // get the table names 4 $tableNames = $metadata->getTableNames(); 5 6 foreach ($tableNames as $tableName) { 7 echo ’In Table ’ . $tableName . PHP_EOL; 8 9 $table = $metadata->getTable($tableName); 10 11 12 echo ’ With columns: ’ . PHP_EOL; 13 foreach ($table->getColumns() as $column) { 14 echo ’ ’ . $column->getName() 15 . ’ -> ’ . $column->getDataType() 16 . PHP_EOL; 17 } 18 19 echo PHP_EOL; 20 echo ’ With constraints: ’ . PHP_EOL; 21 22 foreach ($metadata->getConstraints($tableName) as $constraint) { 23 /** @var $constraint ZendDbMetadataObjectConstraintObject */ 24 echo ’ ’ . $constraint->getName() 25 . ’ -> ’ . $constraint->getType() 26 . PHP_EOL; 27 if (!$constraint->hasColumns()) { 28 continue; 29 } 30 echo ’ column: ’ . implode(’, ’, $constraint->getColumns()); 31 if ($constraint->isForeignKey()) { 32 $fkCols = array(); 33 foreach ($constraint->getReferencedColumns() as $refColumn) { 34 $fkCols[] = $constraint->getReferencedTableName() . ’.’ . $refColumn; 35 } 36 echo ’ => ’ . implode(’, ’, $fkCols); 37 } 38 echo PHP_EOL; 39 40 } 41 42 echo ’----’ . PHP_EOL; 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 class ZendDbMetadataObjectTableObject 2 { 3 public function __construct($name); 4 public function setColumns(array $columns); 5 public function getColumns(); 6 public function setConstraints($constraints); 7 public function getConstraints(); 8 public function setName($name); 9 public function getName(); 10 } 386 Chapter 89. ZendDbMetadata
  • 427. Zend Framework 2 Documentation, Release 2.3.1dev The ColumnObject: 1 class ZendDbMetadataObjectColumnObject { 2 public function __construct($name, $tableName, $schemaName = null); 3 public function setName($name); 4 public function getName(); 5 public function getTableName(); 6 public function setTableName($tableName); 7 public function setSchemaName($schemaName); 8 public function getSchemaName(); 9 public function getOrdinalPosition(); 10 public function setOrdinalPosition($ordinalPosition); 11 public function getColumnDefault(); 12 public function setColumnDefault($columnDefault); 13 public function getIsNullable(); 14 public function setIsNullable($isNullable); 15 public function isNullable(); 16 public function getDataType(); 17 public function setDataType($dataType); 18 public function getCharacterMaximumLength(); 19 public function setCharacterMaximumLength($characterMaximumLength); 20 public function getCharacterOctetLength(); 21 public function setCharacterOctetLength($characterOctetLength); 22 public function getNumericPrecision(); 23 public function setNumericPrecision($numericPrecision); 24 public function getNumericScale(); 25 public function setNumericScale($numericScale); 26 public function getNumericUnsigned(); 27 public function setNumericUnsigned($numericUnsigned); 28 public function isNumericUnsigned(); 29 public function getErratas(); 30 public function setErratas(array $erratas); 31 public function getErrata($errataName); 32 public function setErrata($errataName, $errataValue); 33 } The ConstraintObject: 1 class ZendDbMetadataObjectConstraintObject 2 { 3 public function __construct($name, $tableName, $schemaName = null); 4 public function setName($name); 5 public function getName(); 6 public function setSchemaName($schemaName); 7 public function getSchemaName(); 8 public function getTableName(); 9 public function setTableName($tableName); 10 public function setType($type); 11 public function getType(); 12 public function hasColumns(); 13 public function getColumns(); 14 public function setColumns(array $columns); 15 public function getReferencedTableSchema(); 16 public function setReferencedTableSchema($referencedTableSchema); 17 public function getReferencedTableName(); 18 public function setReferencedTableName($referencedTableName); 19 public function getReferencedColumns(); 20 public function setReferencedColumns(array $referencedColumns); 21 public function getMatchOption(); 89.1. Basic Usage 387
  • 428. Zend Framework 2 Documentation, Release 2.3.1dev 22 public function setMatchOption($matchOption); 23 public function getUpdateRule(); 24 public function setUpdateRule($updateRule); 25 public function getDeleteRule(); 26 public function setDeleteRule($deleteRule); 27 public function getCheckClause(); 28 public function setCheckClause($checkClause); 29 public function isPrimaryKey(); 30 public function isUnique(); 31 public function isForeignKey(); 32 public function isCheck(); 33 34 } The TriggerObject: 1 class ZendDbMetadataObjectTriggerObject 2 { 3 public function getName(); 4 public function setName($name); 5 public function getEventManipulation(); 6 public function setEventManipulation($eventManipulation); 7 public function getEventObjectCatalog(); 8 public function setEventObjectCatalog($eventObjectCatalog); 9 public function getEventObjectSchema(); 10 public function setEventObjectSchema($eventObjectSchema); 11 public function getEventObjectTable(); 12 public function setEventObjectTable($eventObjectTable); 13 public function getActionOrder(); 14 public function setActionOrder($actionOrder); 15 public function getActionCondition(); 16 public function setActionCondition($actionCondition); 17 public function getActionStatement(); 18 public function setActionStatement($actionStatement); 19 public function getActionOrientation(); 20 public function setActionOrientation($actionOrientation); 21 public function getActionTiming(); 22 public function setActionTiming($actionTiming); 23 public function getActionReferenceOldTable(); 24 public function setActionReferenceOldTable($actionReferenceOldTable); 25 public function getActionReferenceNewTable(); 26 public function setActionReferenceNewTable($actionReferenceNewTable); 27 public function getActionReferenceOldRow(); 28 public function setActionReferenceOldRow($actionReferenceOldRow); 29 public function getActionReferenceNewRow(); 30 public function setActionReferenceNewRow($actionReferenceNewRow); 31 public function getCreated(); 32 public function setCreated($created); 33 } 388 Chapter 89. ZendDbMetadata
  • 429. CHAPTER 90 Dumping Variables The static method ZendDebugDebug::dump() prints or returns information about an expression. This simple technique of debugging is common because it is easy to use in an ad hoc fashion and requires no initialization, special tools, or debugging environment. 90.1 Example of dump() method 1 ZendDebugDebug::dump($var, $label = null, $echo = true); The $var argument specifies the expression or variable about which the ZendDebugDebug::dump() method outputs information. The $label argument is a string to be prepended to the output of ZendDebugDebug::dump(). It may be useful, for example, to use labels if you are dumping information about multiple variables on a given screen. The boolean $echo argument specifies whether the output of ZendDebugDebug::dump() is echoed or not. If TRUE, the output is echoed. Regardless of the value of the $echo argument, the return value of this method contains the output. It may be helpful to understand that ZendDebugDebug::dump() method wraps the PHP function var_dump(). If the output stream is detected as a web presentation, the output of var_dump() is escaped using htmlspecialchars() and wrapped with (X)HTML <pre> tags. Tip: Debugging with ZendLog Using ZendDebugDebug::dump() is best for ad hoc debugging during software development. You can add code to dump a variable and then remove the code very quickly. Also consider the ZendLog component when writing more permanent debugging code. For example, you can use the DEBUG log level and the stream log writer to output the string returned by ZendDebugDebug::dump(). 389
  • 430. Zend Framework 2 Documentation, Release 2.3.1dev 390 Chapter 90. Dumping Variables
  • 431. CHAPTER 91 Introduction to ZendDi 91.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. 91.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 391
  • 432. Zend Framework 2 Documentation, Release 2.3.1dev 392 Chapter 91. Introduction to ZendDi
  • 433. CHAPTER 92 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 namespace MyLibrary 2 { 3 class DbAdapter 4 { 5 protected $username = null; 6 protected $password = null; 7 public function __construct($username, $password) 8 { 9 $this->username = $username; 10 $this->password = $password; 11 } 12 } 13 } 14 15 namespace MyMovieApp 16 { 17 class MovieFinder 18 { 19 protected $dbAdapter = null; 20 public function __construct(MyLibraryDbAdapter $dbAdapter) 21 { 22 $this->dbAdapter = $dbAdapter; 23 } 24 } 25 26 class MovieLister 27 { 28 protected $movieFinder = null; 29 public function __construct(MovieFinder $movieFinder) 30 { 31 $this->movieFinder = $movieFinder; 32 } 33 } 34 } With the above code, you find yourself writing the following to wire and utilize this code: 393
  • 434. Zend Framework 2 Documentation, Release 2.3.1dev 1 // $config object is assumed 2 3 $dbAdapter = new MyLibraryDbAdapter($config->username, $config->password); 4 $movieFinder = new MyMovieAppMovieFinder($dbAdapter); 5 $movieLister = new MyMovieAppMovieLister($movieFinder); 6 foreach ($movieLister as $movie) { 7 // iterate and display $movie 8 } 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 candi- date for using ZendDi. The usage is as simple as: 1 // inside a bootstrap somewhere 2 $di = new ZendDiDi(); 3 $di->instanceManager()->setParameters(’MyLibraryDbAdapter’, array( 4 ’username’ => $config->username, 5 ’password’ => $config->password 6 )); 7 8 // inside each controller 9 $movieLister = $di->get(’MyMovieAppMovieLister’); 10 foreach ($movieLister as $movie) { 11 // iterate and display $movie 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: 1 public function __construct(DefinitionList $definitions = null, InstanceManager $instanceManager 2 { 3 $this->definitions = ($definitions) ?: new DefinitionList(new DefinitionRuntimeDefinition()) 4 $this->instanceManager = ($instanceManager) ?: new InstanceManager(); 5 6 if ($config) { 7 $this->configure($config); 8 } 9 } This means that when $di->get() is called, it will be consulting the RuntimeDefinition, which uses reflection to un- derstand 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(): 1 // inside each controller 2 $di = new ZendDiDi(); 3 $movieLister = $di->get(’MyMovieAppMovieLister’, array( 4 ’username’ => $config->username, 5 ’password’ => $config->password 6 )); 7 foreach ($movieLister as $movie) { 394 Chapter 92. ZendDi Quickstart
  • 435. Zend Framework 2 Documentation, Release 2.3.1dev 8 // iterate and display $movie 9 } 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(). 395
  • 436. Zend Framework 2 Documentation, Release 2.3.1dev 396 Chapter 92. ZendDi Quickstart
  • 437. CHAPTER 93 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. 93.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. 93.2 RuntimeDefinition The default DefinitionList instantiated by ZendDiDi, when no other DefinitionList is provided, has as Defini- tionRuntimeDefinition 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 public function __construct(IntrospectionStrategy $introspectionStrategy = null, array $explicitClass 2 { 3 $this->introspectionStrategy = ($introspectionStrategy) ?: new IntrospectionStrategy(); 4 if ($explicitClasses) { 5 $this->setExplicitClasses($explicitClasses); 6 } 7 } 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: • Whether or not to use Annotations (Annotations are expensive and off by default, read more about these in the Annotations section) 397
  • 438. Zend Framework 2 Documentation, Release 2.3.1dev • 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 regis- tered, this is a list of patterns. The constructor for the IntrospectionStrategy looks like this: 1 public function __construct(AnnotationManager $annotationManager = null) 2 { 3 $this->annotationManager = ($annotationManager) ?: $this->createDefaultAnnotationManager(); 4 } 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. 93.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 // in "package name" format 2 $components = array( 3 ’My_MovieApp’, 4 ’My_OtherClasses’, 5 ); 6 7 foreach ($components as $component) { 8 $diCompiler = new ZendDiDefinitionCompilerDefinition; 9 $diCompiler->addDirectory(’/path/to/classes/’ . str_replace(’_’, ’/’, $component)); 10 11 $diCompiler->compile(); 12 file_put_contents( 13 __DIR__ . ’/../data/di/’ . $component . ’-definition.php’, 14 ’<?php return ’ . var_export($diCompiler->toArrayDefinition()->toArray(), true) . ’;’ 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 protected function setupDi(Application $app) 2 { 3 $definitionList = new DefinitionList(array( 4 new DefinitionArrayDefinition(include __DIR__ . ’/path/to/data/di/My_MovieApp-definition.php 5 new DefinitionArrayDefinition(include __DIR__ . ’/path/to/data/di/My_OtherClasses-definition 6 $runtime = new DefinitionRuntimeDefinition(), 398 Chapter 93. ZendDi Definition
  • 439. Zend Framework 2 Documentation, Release 2.3.1dev 7 )); 8 $di = new Di($definitionList, null, new Configuration($this->config->di)); 9 $di->instanceManager()->addTypePreference(’ZendDiLocatorInterface’, $di); 10 $app->setLocator($di); 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. 93.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 93.4. ClassDefinition 399
  • 440. Zend Framework 2 Documentation, Release 2.3.1dev 400 Chapter 93. ZendDi Definition
  • 441. CHAPTER 94 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. 94.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 namespace MyLibrary 2 { 3 class DbAdapter 4 { 5 protected $username = null; 6 protected $password = null; 7 public function __construct($username, $password) 8 { 9 $this->username = $username; 10 $this->password = $password; 11 } 12 } 13 } 14 15 namespace MyMovieApp 16 { 17 class MovieFinder 18 { 19 protected $dbAdapter = null; 20 public function __construct(MyLibraryDbAdapter $dbAdapter) 21 { 22 $this->dbAdapter = $dbAdapter; 23 } 24 } 25 26 class MovieLister 27 { 28 protected $movieFinder = null; 401
  • 442. Zend Framework 2 Documentation, Release 2.3.1dev 29 public function __construct(MovieFinder $movieFinder) 30 { 31 $this->movieFinder = $movieFinder; 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 // setting instance configuration into the instance manager 2 $di->instanceManager()->setParameters(’MyLibraryDbAdapter’, array( 3 ’username’ => ’myusername’, 4 ’password’ => ’mypassword’ 5 )); 6 7 // forcing a particular dependency to be used by the instance manager 8 $di->instanceManager()->setParameters(’MyMovieAppMovieFinder’, array( 9 ’dbAdapter’ => new MyLibraryDbAdapter(’myusername’, ’mypassword’) 10 )); 11 12 // passing instance parameters at call time 13 $movieLister = $di->get(’MyMovieAppMovieLister’, array( 14 ’username’ => $config->username, 15 ’password’ => $config->password 16 )); 17 18 // passing a specific instance at call time 19 $movieLister = $di->get(’MyMovieAppMovieLister’, array( 20 ’dbAdapter’ => new MyLibraryDbAdapter(’myusername’, ’mypassword’) 21 )); 94.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 namespace MyMovieApp 2 { 3 interface MovieFinderInterface 4 { 5 // methods required for this type 6 } 7 8 class GenericMovieFinder implements MovieFinderInterface 9 { 10 protected $dbAdapter = null; 402 Chapter 94. ZendDi InstanceManager
  • 443. Zend Framework 2 Documentation, Release 2.3.1dev 11 public function __construct(MyLibraryDbAdapter $dbAdapter) 12 { 13 $this->dbAdapter = $dbAdapter; 14 } 15 } 16 17 class MovieLister 18 { 19 protected $movieFinder = null; 20 public function __construct(MovieFinderInterface $movieFinder) 21 { 22 $this->movieFinder = $movieFinder; 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 $di->instanceManager()->addTypePreference(’MyMovieAppMovieFinderInterface’, ’MyMovieAppGenericMovie 2 // assuming all instance config for username, password is setup 3 $di->get(’MyMovieAppMovieLister’); 94.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 // add alias for short naming 6 $im->addAlias(’movielister’, ’MyMovieAppMovieLister’); 7 8 // add aliases for specific instances 9 $im->addAlias(’dbadapter-readonly’, ’MyLibraryDbAdapter’, array( 10 ’username’ => $config->db->readAdapter->username, 11 ’password’ => $config->db->readAdapter->password, 12 )); 13 $im->addAlias(’dbadapter-readwrite’, ’MyLibraryDbAdapter’, array( 14 ’username’ => $config->db->readWriteAdapter->username, 94.3. Aliases 403
  • 444. Zend Framework 2 Documentation, Release 2.3.1dev 15 ’password’ => $config->db->readWriteAdapter->password, 16 )); 17 18 // set a default type to use, pointing to an alias 19 $im->addTypePreference(’MyLibraryDbAdapter’, ’dbadapter-readonly’); 20 21 $movieListerRead = $di->get(’MyMovieAppMovieLister’); 22 $movieListerReadWrite = $di->get(’MyMovieAppMovieLister’, array(’dbAdapter’ => ’dbadapter-readwrite’ 404 Chapter 94. ZendDi InstanceManager
  • 445. CHAPTER 95 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 $config = array( 2 ’definition’ => array( 3 ’compiler’ => array(/* @todo compiler information */), 4 ’runtime’ => array(/* @todo runtime information */), 5 ’class’ => array( 6 ’instantiator’ => ’’, // the name of the instantiator, by default this is __construct 7 ’supertypes’ => array(), // an array of supertypes the class implements 8 ’methods’ => array( 9 ’setSomeParameter’ => array( // a method name 10 ’parameterName’ => array( 11 ’name’, // string parameter name 12 ’type’, // type or null 13 ’is-required’ // bool 14 ) 15 ) 16 17 ) 18 ) 19 ) 20 ); 405
  • 446. Zend Framework 2 Documentation, Release 2.3.1dev 406 Chapter 95. ZendDi Configuration
  • 447. CHAPTER 96 ZendDi Debugging & Complex Use Cases 96.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’)); 96.2 Complex Use Cases 96.2.1 Interface Injection 1 namespace FooBar { 2 class Baz implements BamAwareInterface 3 { 4 public $bam; 5 6 public function setBam(Bam $bam) 7 { 8 $this->bam = $bam; 9 } 10 } 11 class Bam 12 { 13 } 14 interface BamAwareInterface 15 { 16 public function setBam(Bam $bam); 17 } 18 } 19 20 namespace { 21 include ’zf2bootstrap.php’; 22 $di = new ZendDiDi; 407
  • 448. Zend Framework 2 Documentation, Release 2.3.1dev 23 $baz = $di->get(’FooBarBaz’); 24 } 96.2.2 Setter Injection with Class Definition 1 namespace FooBar { 2 class Baz 3 { 4 public $bam; 5 6 public function setBam(Bam $bam) 7 { 8 $this->bam = $bam; 9 } 10 } 11 class Bam { 12 } 13 } 14 15 namespace { 16 $di = new ZendDiDi; 17 $di->configure(new ZendDiConfig(array( 18 ’definition’ => array( 19 ’class’ => array( 20 ’FooBarBaz’ => array( 21 ’setBam’ => array(’required’ => true) 22 ) 23 ) 24 ) 25 ))); 26 $baz = $di->get(’FooBarBaz’); 27 } 96.2.3 Multiple Injections To A Single Injection Point 1 namespace Application { 2 class Page 3 { 4 public $blocks; 5 6 public function addBlock(Block $block) 7 { 8 $this->blocks[] = $block; 9 } 10 } 11 interface Block 12 { 13 } 14 } 15 16 namespace MyModule { 17 class BlockOne implements ApplicationBlock {} 18 class BlockTwo implements ApplicationBlock {} 19 } 20 408 Chapter 96. ZendDi Debugging & Complex Use Cases
  • 449. Zend Framework 2 Documentation, Release 2.3.1dev 21 namespace { 22 include ’zf2bootstrap.php’; 23 $di = new ZendDiDi; 24 $di->configure(new ZendDiConfig(array( 25 ’definition’ => array( 26 ’class’ => array( 27 ’ApplicationPage’ => array( 28 ’addBlock’ => array( 29 ’block’ => array(’type’ => ’ApplicationBlock’, ’required’ => true) 30 ) 31 ) 32 ) 33 ), 34 ’instance’ => array( 35 ’ApplicationPage’ => array( 36 ’injections’ => array( 37 ’MyModuleBlockOne’, 38 ’MyModuleBlockTwo’ 39 ) 40 ) 41 ) 42 ))); 43 $page = $di->get(’ApplicationPage’); 44 } 96.2. Complex Use Cases 409
  • 450. Zend Framework 2 Documentation, Release 2.3.1dev 410 Chapter 96. ZendDi Debugging & Complex Use Cases
  • 451. CHAPTER 97 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. 411
  • 452. Zend Framework 2 Documentation, Release 2.3.1dev 412 Chapter 97. Introduction to ZendDom
  • 453. CHAPTER 98 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 (Proto- type’s $$() and Dojo’s dojo.query were both inspirations for the component). 98.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. • 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’ 413
  • 454. Zend Framework 2 Documentation, Release 2.3.1dev 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: 1 <div> 2 <table> 3 <tr> 4 <td class="foo"> 5 <div> 6 Lorem ipsum <span class="bar"> 7 <a href="/foo/bar" id="one">One</a> 8 <a href="/foo/baz" id="two">Two</a> 9 <a href="/foo/bat" id="three">Three</a> 10 <a href="/foo/bla" id="four">Four</a> 11 </span> 12 </div> 13 </td> 14 </tr> 15 </table> 16 </div> 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 $dom = new Query($html); 4 $results = $dom->execute(’.foo .bar a’); 5 6 $count = count($results); // get number of matches: 4 7 foreach ($results as $result) { 8 // $result is a DOMElement 9 } 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. 98.2 Methods Available The ZendDomQuery family of classes have the following methods available. 98.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 a string to query against; ZendDomQuery will then attempt to autodetect the document type. 414 Chapter 98. ZendDomQuery
  • 455. Zend Framework 2 Documentation, Release 2.3.1dev • setEncoding($encoding): specify an encoding string to use. This encoding will be passed to DOMDoc- ument’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. 98.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. 98.2. Methods Available 415
  • 456. Zend Framework 2 Documentation, Release 2.3.1dev 416 Chapter 98. ZendDomQuery
  • 457. CHAPTER 99 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. 99.1 Overview The ZendEscaper component provides one class, ZendEscaperEscaper which in turn, provides five meth- ods 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. 99.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. 417
  • 458. Zend Framework 2 Documentation, Release 2.3.1dev 418 Chapter 99. Introduction to ZendEscaper
  • 459. CHAPTER 100 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 // &lt;script&gt;alert(&quot;zf2&quot;)&lt;/script&gt; 4 echo $escaper->escapeHtml(’<script>alert("zf2")</script>’); 5 // &lt;script&gt;alert&#x28;&quot;zf2&quot;&#x29;&lt;&#x2F;script&gt; 6 echo $escaper->escapeHtmlAttr(’<script>alert("zf2")</script>’); 7 // x3Cscriptx3Ealertx28x22zf2x22x29x3Cx2Fscriptx3E 8 echo $escaper->escapeJs(’<script>alert("zf2")</script>’); 9 // 3C script3E alert28 22 zf222 29 3C 2F script3E 10 echo $escaper->escapeCss(’<script>alert("zf2")</script>’); 11 // %3Cscript%3Ealert%28%22zf2%22%29%3C%2Fscript%3E 12 echo $escaper->escapeUrl(’<script>alert("zf2")</script>’); More detailed examples will be given in later chapters. 100.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() • Javascript: addslashes() or json_encode() • CSS: n/a 419
  • 460. Zend Framework 2 Documentation, Release 2.3.1dev • 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 misrepre- sentations 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. 100.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!): 100.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. 100.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! 420 Chapter 100. Theory of Operation
  • 461. Zend Framework 2 Documentation, Release 2.3.1dev 100.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). 100.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. 100.2. Why Contextual Escaping? 421
  • 462. Zend Framework 2 Documentation, Release 2.3.1dev 422 Chapter 100. Theory of Operation
  • 463. CHAPTER 101 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, a ZendEscaperExceptionInvalidArgumentException will be thrown. 423
  • 464. Zend Framework 2 Documentation, Release 2.3.1dev 424 Chapter 101. Configuring ZendEscaper
  • 465. CHAPTER 102 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 // outputting this without escaping would be a bad idea! 2 $input = ’<script>alert("zf2")</script>’; 3 4 $escaper = new ZendEscaperEscaper(’utf-8’); 5 6 // somewhere in an HTML template 7 <div class="user-provided-input"> 8 <?php 9 echo $escaper->escapeHtml($input); // all safe! 10 ?> 11 </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! 102.1 Examples of Bad HTML Escaping An example of incorrect usage: 1 <?php 2 $input = ’<script>alert("zf2")</script>’; 3 $escaper = new ZendEscaperEscaper(’utf-8’); 4 ?> 5 <?php header(’Content-Type: text/html; charset=ISO-8859-1’); ?> 6 <!DOCTYPE html> 7 <html> 8 <head> 9 <title>Encodings set incorrectly!</title> 10 <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> 11 </head> 12 <body> 13 <?php 14 // Bad! The escaper’s and the document’s encodings are different! 15 echo $escaper->escapeHtml($input); 425
  • 466. Zend Framework 2 Documentation, Release 2.3.1dev 16 ?> 17 </body> 102.2 Examples of Good HTML Escaping An example of correct usage: 1 <?php 2 $input = ’<script>alert("zf2")</script>’; 3 $escaper = new ZendEscaperEscaper(’utf-8’); 4 ?> 5 <?php header(’Content-Type: text/html; charset=UTF-8’); ?> 6 <!DOCTYPE html> 7 <html> 8 <head> 9 <title>Encodings set correctly!</title> 10 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 11 </head> 12 <body> 13 <?php 14 // Good! The escaper’s and the document’s encodings are same! 15 echo $escaper->escapeHtml($input); 16 ?> 17 </body> 426 Chapter 102. Escaping HTML
  • 467. CHAPTER 103 Escaping HTML Attributes Escaping data in the HTML Attribute context is most often done incorrectly, if not overlooked completely by de- velopers. 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. 103.1 Examples of Bad HTML Attribute Escaping An example of incorrect HTML attribute escaping: 1 <?php header(’Content-Type: text/html; charset=UTF-8’); ?> 2 <!DOCTYPE html> 3 <?php 4 $input = <<<INPUT 5 ’ onmouseover=’alert(/ZF2!/); 6 INPUT; 7 /** 8 * NOTE: This is equivalent to using htmlspecialchars($input, ENT_COMPAT) 9 */ 10 $output = htmlspecialchars($input); 11 ?> 12 <html> 13 <head> 14 <title>Single Quoted Attribute</title> 15 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 16 </head> 17 <body> 18 <div> 19 <?php 20 // the span tag will look like: 21 // <span title=’’ onmouseover=’alert(/ZF2!/);’> 22 ?> 23 <span title=’<?php echo $output ?>’> 24 What framework are you using? 25 </span> 26 </div> 27 </body> 28 </html> 427
  • 468. Zend Framework 2 Documentation, Release 2.3.1dev 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 <?php header(’Content-Type: text/html; charset=UTF-8’); ?> 2 <!DOCTYPE html> 3 <?php 4 $input = <<<INPUT 5 faketitle onmouseover=alert(/ZF2!/); 6 INPUT; 7 // Tough luck using proper flags when the title attribute is unquoted! 8 $output = htmlspecialchars($input,ENT_QUOTES); 9 ?> 10 <html> 11 <head> 12 <title>Quoteless Attribute</title> 13 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 14 </head> 15 <body> 16 <div> 17 <?php 18 // the span tag will look like: 19 // <span title=faketitle onmouseover=alert(/ZF2!/);> 20 ?> 21 <span title=<?php echo $output ?>> 22 What framework are you using? 23 </span> 24 </div> 25 </body> 26 </html> The above example shows how it is easy to break out from unquoted attributes in HTML5. 103.2 Examples of Good HTML Attribute Escaping Both of the previous examples can be avoided by simply using the escapeHtmlAttr method: 1 <?php header(’Content-Type: text/html; charset=UTF-8’); ?> 2 <!DOCTYPE html> 3 <?php 4 $input = <<<INPUT 5 faketitle onmouseover=alert(/ZF2!/); 6 INPUT; 7 $escaper = new ZendEscaperEscaper(’utf-8’); 8 $output = $escaper->escapeHtmlAttr($input); 9 ?> 10 <html> 11 <head> 12 <title>Quoteless Attribute</title> 13 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 14 </head> 15 <body> 16 <div> 17 <?php 18 // the span tag will look like: 428 Chapter 103. Escaping HTML Attributes
  • 469. Zend Framework 2 Documentation, Release 2.3.1dev 19 // <span title=faketitle&#x20;onmouseover&#x3D;alert&#x28;&#x2F;ZF2&#x21;&#x2F;&#x29;&#x3B;> 20 ?> 21 <span title=<?php echo $output ?>> 22 What framework are you using? 23 </span> 24 </div> 25 </body> 26 </html> In the above example, the malicious input from the attacker becomes completely harmless as we used proper HTML attribute escaping! 103.2. Examples of Good HTML Attribute Escaping 429
  • 470. Zend Framework 2 Documentation, Release 2.3.1dev 430 Chapter 103. Escaping HTML Attributes
  • 471. CHAPTER 104 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. 104.1 Examples of Bad Javascript Escaping An example of incorrect Javascript escaping: 1 <?php header(’Content-Type: application/xhtml+xml; charset=UTF-8’); ?> 2 <!DOCTYPE html> 3 <?php 4 $input = <<<INPUT 5 bar&quot;; alert(&quot;Meow!&quot;); var xss=&quot;true 6 INPUT; 7 $output = json_encode($input); 8 ?> 9 <html xmlns="http://www.w3.org/1999/xhtml"> 10 <head> 11 <title>Unescaped Entities</title> 12 <meta charset="UTF-8"/> 13 <script type="text/javascript"> 14 <?php 15 // this will result in 16 // var foo = "bar&quot;; alert(&quot;Meow!&quot;); var xss=&quot;true"; 17 ?> 18 var foo = <?php echo $output ?>; 19 </script> 20 </head> 21 <body> 22 <p>json_encode() is not good for escaping javascript!</p> 23 </body> 24 </html> 431
  • 472. Zend Framework 2 Documentation, Release 2.3.1dev 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. 104.2 Examples of Good Javascript Escaping By using the escapeJs method in the Javascript context, such attacks can be prevented: 1 <?php header(’Content-Type: text/html; charset=UTF-8’); ?> 2 <!DOCTYPE html> 3 <?php 4 $input = <<<INPUT 5 bar&quot;; alert(&quot;Meow!&quot;); var xss=&quot;true 6 INPUT; 7 $escaper = new ZendEscaperEscaper(’utf-8’); 8 $output = $escaper->escapeJs($input); 9 ?> 10 <html xmlns="http://www.w3.org/1999/xhtml"> 11 <head> 12 <title>Escaped Entities</title> 13 <meta charset="UTF-8"/> 14 <script type="text/javascript"> 15 <?php 16 // this will look like 17 // var foo = barx26quotx3Bx3Bx20alertx28x26quotx3BMeowx21x26quotx3Bx29x3Bx20var 18 ?> 19 var foo = <?php echo $output ?>; 20 </script> 21 </head> 22 <body> 23 <p>ZendEscaperEscaper::escapeJs() is good for escaping javascript!</p> 24 </body> 25 </html> In the above example, the Javascript parser will most likely report a SyntaxError, but at least the targeted applica- tion remains safe from such attacks. 432 Chapter 104. Escaping Javascript
  • 473. CHAPTER 105 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. 105.1 Examples of Bad CSS Escaping In most cases developers forget to escape CSS completely: 1 <?php header(’Content-Type: application/xhtml+xml; charset=UTF-8’); ?> 2 <!DOCTYPE html> 3 <?php 4 $input = <<<INPUT 5 body { 6 background-image: url(’http://example.com/foo.jpg?</style><script>alert(1)</script>’); 7 } 8 INPUT; 9 ?> 10 <html xmlns="http://www.w3.org/1999/xhtml"> 11 <head> 12 <title>Unescaped CSS</title> 13 <meta charset="UTF-8"/> 14 <style> 15 <?php echo $input; ?> 16 </style> 17 </head> 18 <body> 19 <p>User controlled CSS needs to be properly escaped!</p> 20 </body> 21 </html> In the above example, by failing to escape the user provided CSS, an attacker can execute an XSS attack fairly easily. 105.2 Examples of Good CSS Escaping By using escapeCss method in the CSS context, such attacks can be prevented: 1 <?php header(’Content-Type: application/xhtml+xml; charset=UTF-8’); ?> 2 <!DOCTYPE html> 3 <?php 4 $input = <<<INPUT 433
  • 474. Zend Framework 2 Documentation, Release 2.3.1dev 5 body { 6 background-image: url(’http://example.com/foo.jpg?</style><script>alert(1)</script>’); 7 } 8 INPUT; 9 $escaper = new ZendEscaperEscaper(’utf-8’); 10 $output = $escaper->escapeCss($input); 11 ?> 12 <html xmlns="http://www.w3.org/1999/xhtml"> 13 <head> 14 <title>Escaped CSS</title> 15 <meta charset="UTF-8"/> 16 <style> 17 <?php 18 // output will look something like 19 // body20 7B A 20 20 20 20 background2D image3A 20 url28 ... 20 echo $output; 21 ?> 22 </style> 23 </head> 24 <body> 25 <p>User controlled CSS needs to be properly escaped!</p> 26 </body> 27 </html> By properly escaping user controlled CSS, we can prevent XSS attacks in our web applications. 434 Chapter 105. Escaping Cascading Style Sheets
  • 475. CHAPTER 106 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. 106.1 Examples of Bad URL Escaping XSS attacks are easy if data inserted into URLs is not escaped properly: 1 <?php header(’Content-Type: application/xhtml+xml; charset=UTF-8’); ?> 2 <!DOCTYPE html> 3 <?php 4 $input = <<<INPUT 5 " onmouseover="alert(’zf2’) 6 INPUT; 7 ?> 8 <html xmlns="http://www.w3.org/1999/xhtml"> 9 <head> 10 <title>Unescaped URL data</title> 11 <meta charset="UTF-8"/> 12 </head> 13 <body> 14 <a href="http://example.com/?name=<?php echo $input; ?>">Click here!</a> 15 </body> 16 </html> 106.2 Examples of Good URL Escaping By properly escaping data in URLs by using escapeUrl, we can prevent XSS attacks: 1 <?php header(’Content-Type: application/xhtml+xml; charset=UTF-8’); ?> 2 <!DOCTYPE html> 3 <?php 4 $input = <<<INPUT 5 " onmouseover="alert(’zf2’) 6 INPUT; 7 $escaper = new ZendEscaperEscaper(’utf-8’); 8 $output = $escaper->escapeUrl($input); 9 ?> 435
  • 476. Zend Framework 2 Documentation, Release 2.3.1dev 10 <html xmlns="http://www.w3.org/1999/xhtml"> 11 <head> 12 <title>Unescaped URL data</title> 13 <meta charset="UTF-8"/> 14 </head> 15 <body> 16 <a href="http://example.com/?name=<?php echo $output; ?>">Click here!</a> 17 </body> 18 </html> 436 Chapter 106. Escaping URLs
  • 477. CHAPTER 107 The EventManager 107.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. 107.2 Quick Start Typically, you will compose an EventManager instance in a class. 1 use ZendEventManagerEventManagerInterface; 2 use ZendEventManagerEventManager; 3 use ZendEventManagerEventManagerAwareInterface; 4 5 class Foo implements EventManagerAwareInterface 6 { 7 protected $events; 8 9 public function setEventManager(EventManagerInterface $events) 10 { 11 $events->setIdentifiers(array( 12 __CLASS__, 13 get_called_class(), 14 )); 15 $this->events = $events; 16 return $this; 17 } 18 19 public function getEventManager() 20 { 21 if (null === $this->events) { 22 $this->setEventManager(new EventManager()); 23 } 24 return $this->events; 437
  • 478. Zend Framework 2 Documentation, Release 2.3.1dev 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 class Foo 2 { 3 // ... assume events definition from above 4 5 public function bar($baz, $bat = null) 6 { 7 $params = compact(’baz’, ’bat’); 8 $this->getEventManager()->trigger(__FUNCTION__, $this, $params); 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 $log = LogFactory($someConfig); 4 $foo = new Foo(); 5 $foo->getEventManager()->attach(’bar’, function ($e) use ($log) { 6 $event = $e->getName(); 7 $target = get_class($e->getTarget()); 8 $params = json_encode($e->getParams()); 9 10 $log->info(sprintf( 11 ’%s called on %s, using params %s’, 12 $event, 13 $target, 14 $params 15 )); 16 }); 17 18 // Results in log message: 19 $foo->bar(’baz’, ’bat’); 20 // 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 they do to normal event managers; the call to attach is identical to the EventManager, 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 con- structor, may be used to identify an instance when using a SharedEventCollection. As an example, assuming 438 Chapter 107. The EventManager
  • 479. Zend Framework 2 Documentation, Release 2.3.1dev 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 $log = LogFactory($someConfig); 6 $events->attach(’Foo’, ’bar’, function ($e) use ($log) { 7 $event = $e->getName(); 8 $target = get_class($e->getTarget()); 9 $params = json_encode($e->getParams()); 10 11 $log->info(sprintf( 12 ’%s called on %s, using params %s’, 13 $event, 14 $target, 15 $params 16 )); 17 }); 18 19 // Later, instantiate Foo: 20 $foo = new Foo(); 21 $foo->getEventManager()->setSharedManager($events); 22 23 // And we can still trigger the above event: 24 $foo->bar(’baz’, ’bat’); 25 // results in log message: 26 // 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. 107.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. Attaching to many events at once 1 $events = new EventManager(); 2 $events->attach(array(’these’, ’are’, ’event’, ’names’), $callback); Note that if you specify a priority, that priority will be used for all events specified. 107.2. Quick Start 439
  • 480. Zend Framework 2 Documentation, Release 2.3.1dev Attaching using the wildcard 1 $events = new EventManager(); 2 $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 $events = new SharedEventManager(); 2 // Attach to many events on the context "foo" 3 $events->attach(’foo’, array(’these’, ’are’, ’event’, ’names’), $callback); 4 5 // Attach to many events on the contexts "foo" and "bar" 6 $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 $events = new SharedEventManager(); 2 // Attach to all events on the context "foo" 3 $events->attach(’foo’, ’*’, $callback); 4 5 // Attach to all events on the contexts "foo" and "bar" 6 $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. 107.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. 107.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. 440 Chapter 107. The EventManager
  • 481. Zend Framework 2 Documentation, Release 2.3.1dev 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 as- sociative 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. 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. 107.4. Available Methods 441
  • 482. Zend Framework 2 Documentation, Release 2.3.1dev 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. 107.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 class ValueObject 2 { 3 // assume a composed event manager 4 5 function inject(array $values) 6 { 7 $argv = compact(’values’); 8 $argv = $this->getEventManager()->prepareArgs($argv); 9 $this->getEventManager()->trigger(__FUNCTION__, $this, $argv); 10 $date = isset($argv[’values’][’date’]) ? $argv[’values’][’date’] : new DateTime(’now’); 11 12 // ... 13 } 14 } 15 16 $v = new ValueObject(); 17 18 $v->getEventManager()->attach(’inject’, function($e) { 19 $values = $e->getParam(’values’); 20 if (!$values) { 21 return; 22 } 23 24 if (!isset($values[’date’])) { 25 $values[’date’] = new DateTime(’now’); 26 } else { 27 $values[’date’] = new Datetime($values[’date’]); 28 } 29 30 $e->setParam(’values’, $values); 31 }); 32 442 Chapter 107. The EventManager
  • 483. Zend Framework 2 Documentation, Release 2.3.1dev 33 $v->inject(array( 34 ’date’ => ’2011-08-10 15:30:29’, 35 )); 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 $listener = function($e) { 2 // do some work 3 4 // Stop propagation and return a response 5 $e->stopPropagation(true); 6 return $response; 7 }; Alternately, we could do the check from the method triggering the event. 1 class Foo implements DispatchableInterface 2 { 3 // assume composed event manager 4 5 public function dispatch(Request $request, Response $response = null) 6 { 7 $argv = compact(’request’, ’response’); 8 $results = $this->getEventManager()->triggerUntil(__FUNCTION__, $this, $argv, function($v) { 9 return ($v instanceof Response); 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 class Foo implements DispatchableInterface 2 { 3 // assume composed event manager 4 5 public function dispatch(Request $request, Response $response = null) 6 { 7 $argv = compact(’request’, ’response’); 8 $results = $this->getEventManager()->triggerUntil(__FUNCTION__, $this, $argv, function($v) { 9 return ($v instanceof Response); 10 }); 11 12 // Test if execution was halted, and return last result: 13 if ($results->stopped()) { 14 return $results->last(); 15 } 16 17 // continue... 18 } 19 } 107.5. Examples 443
  • 484. Zend Framework 2 Documentation, Release 2.3.1dev 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 class SomeValueObject 2 { 3 // assume it composes an event manager 4 5 public function get($id) 6 { 7 $params = compact(’id’); 8 $results = $this->getEventManager()->trigger(’get.pre’, $this, $params); 9 10 // If an event stopped propagation, return the value 11 if ($results->stopped()) { 12 return $results->last(); 13 } 14 15 // do some work... 16 17 $params[’__RESULT__’] = $someComputedContent; 18 $this->getEventManager()->trigger(’get.post’, $this, $params); 19 } 20 } Now, let’s create a ListenerAggregateInterface that can handle caching for us: 1 use ZendCacheCache; 2 use ZendEventManagerEventManagerInterface; 3 use ZendEventManagerListenerAggregateInterface; 4 use ZendEventManagerEventInterface; 5 6 class CacheListener implements ListenerAggregateInterface 7 { 8 protected $cache; 9 10 protected $listeners = array(); 11 12 public function __construct(Cache $cache) 13 { 14 $this->cache = $cache; 15 } 16 17 public function attach(EventManagerInterface $events) 18 { 19 $this->listeners[] = $events->attach(’get.pre’, array($this, ’load’), 100); 20 $this->listeners[] = $events->attach(’get.post’, array($this, ’save’), -100); 21 } 22 444 Chapter 107. The EventManager
  • 485. Zend Framework 2 Documentation, Release 2.3.1dev 23 public function detach(EventManagerInterface $events) 24 { 25 foreach ($this->listeners as $index => $listener) { 26 if ($events->detach($listener)) { 27 unset($this->listeners[$index]); 28 } 29 } 30 } 31 32 public function load(EventInterface $e) 33 { 34 $id = get_class($e->getTarget()) . ’-’ . json_encode($e->getParams()); 35 if (false !== ($content = $this->cache->load($id))) { 36 $e->stopPropagation(true); 37 return $content; 38 } 39 } 40 41 public function save(EventInterface $e) 42 { 43 $params = $e->getParams(); 44 $content = $params[’__RESULT__’]; 45 unset($params[’__RESULT__’]); 46 47 $id = get_class($e->getTarget()) . ’-’ . json_encode($params); 48 $this->cache->save($content, $id); 49 } 50 } We can then attach the aggregate to an instance. 1 $value = new SomeValueObject(); 2 $cacheListener = new CacheListener($cache); 3 $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. 107.5. Examples 445
  • 486. Zend Framework 2 Documentation, Release 2.3.1dev 446 Chapter 107. The EventManager
  • 487. CHAPTER 108 Using Exceptions Zend_Exception is simply the base class for all exceptions thrown within Zend Framework. 108.1 Catching an Exception The following code listing demonstrates how to catch an exception thrown in a Zend Framework class: 1 try { 2 // Calling ZendLoaderLoader::loadClass() with a non-existent class will cause 3 // an exception to be thrown in ZendLoaderLoader: 4 ZendLoaderLoader::loadClass(’NonExistentClass’); 5 } catch (Zend_Exception $e) { 6 echo "Caught exception: " . get_class($e) . "n"; 7 echo "Message: " . $e->getMessage() . "n"; 8 // Other code to recover from the error 9 } Zend_Exception can be used as a catch-all exception class in a catch block to trap all exceptions thrown by Zend Framework classes. This can be useful when the program can not recover by catching a specific exception type. The documentation for each Zend Framework component and class will contain specific information on which methods throw exceptions, the circumstances that cause an exception to be thrown, and the various exception types that may be thrown. 447
  • 488. Zend Framework 2 Documentation, Release 2.3.1dev 448 Chapter 108. Using Exceptions
  • 489. CHAPTER 109 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 writ- ing 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. 109.1 Reading RSS Feed Data 1 // Fetch the latest Slashdot headlines 2 try { 3 $slashdotRss = 4 ZendFeedReaderReader::import(’http://rss.slashdot.org/Slashdot/slashdot’); 5 } catch (ZendFeedReaderExceptionRuntimeException $e) { 6 // feed import failed 7 echo "Exception caught importing feed: {$e->getMessage()}n"; 8 exit; 9 } 10 11 // Initialize the channel/feed data array 12 $channel = array( 13 ’title’ => $slashdotRss->getTitle(), 14 ’link’ => $slashdotRss->getLink(), 15 ’description’ => $slashdotRss->getDescription(), 16 ’items’ => array() 17 ); 18 19 // Loop over each channel item/entry and store relevant data for each 449
  • 490. Zend Framework 2 Documentation, Release 2.3.1dev 20 foreach ($slashdotRss as $item) { 21 $channel[’items’][] = array( 22 ’title’ => $item->getTitle(), 23 ’link’ => $item->getLink(), 24 ’description’ => $item->getDescription() 25 ); 26 } 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. 450 Chapter 109. Introduction to ZendFeed
  • 491. CHAPTER 110 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 // importing a feed from a text file 2 $feedFromFile = ZendFeedReaderReader::importFile(’feed.xml’); 3 4 // importing a feed from a PHP string variable 5 $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. 110.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 // dump the feed to standard output 4 print $feed->saveXml(); 451
  • 492. Zend Framework 2 Documentation, Release 2.3.1dev 452 Chapter 110. Importing Feeds
  • 493. CHAPTER 111 Retrieving Feeds from Web Pages 111.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 $rssFeed = null; 2 $feedLinks = ZendFeedReaderReader::findFeedLinks(’http://www.example.com/news.html’); 3 foreach ($feedLinks as $link) { 4 if (stripos($link[’type’], ’application/rss+xml’) !== false) { 5 $rssFeed = $link[’href’]; 6 break; 7 } 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 $rssFeed = null; 2 $feedLinks = ZendFeedReaderReader::findFeedLinks(’http://www.example.com/news.html’); 3 $firstAtomFeed = $feedLinks->atom; 453
  • 494. Zend Framework 2 Documentation, Release 2.3.1dev 454 Chapter 111. Retrieving Feeds from Web Pages
  • 495. CHAPTER 112 Consuming an RSS Feed 112.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. 112.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 $author = $channel->getAuthor(); 2 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 foreach ($channel as $item) { 2 echo $item->getTitle() . "n"; 3 } 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). Required channel elements: • title- The name of the channel 455
  • 496. Zend Framework 2 Documentation, Release 2.3.1dev • 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 if ($item->getPropname()) { 2 // ... proceed. 3 } 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. Please see the official RSS 2.0 specification for further information. 456 Chapter 112. Consuming an RSS Feed
  • 497. CHAPTER 113 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. 113.1 Basic Use of an Atom Feed Read an Atom feed and print the title and summary of each entry: 1 $feed = ZendFeedReaderReader::import(’http://atom.example.com/feed/’); 2 echo ’The feed contains ’ . $feed->count() . ’ entries.’ . "nn"; 3 foreach ($feed as $entry) { 4 echo ’Title: ’ . $entry->getTitle() . "n"; 5 echo ’Description: ’ . $entry->getDescription() . "n"; 6 echo ’URL: ’ . $entry->getLink() . "nn"; 7 } 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 The link property of an atom entry typically has an href attribute. • summary- A summary of this entry’s content 457
  • 498. Zend Framework 2 Documentation, Release 2.3.1dev • 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/. 458 Chapter 113. Consuming an Atom Feed
  • 499. CHAPTER 114 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. 114.1 Reading a Single-Entry Atom Feed 1 $entry = ZendFeedReaderReader::import(’http://atom.example.com/feed/1’); 2 echo ’Entry title: ’ . $entry->getTitle(); 459
  • 500. Zend Framework 2 Documentation, Release 2.3.1dev 460 Chapter 114. Consuming a Single Atom Entry
  • 501. CHAPTER 115 ZendFeed and Security 115.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 appli- cation or before storing that data in some storage engine (be it a simple file, a database...). 115.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 standards- compliant output. The following examples will show a basic usage of HTMLPurifier, but developers are urged to go through and read HTMLPurifier’s documentation. 1 // Setting HTMLPurifier’s options 2 $options = array( 3 // Allow only paragraph tags 4 // and anchor tags wit the href attribute 5 array( 6 ’HTML.Allowed’, 7 ’p,a[href]’ 8 ), 9 // Format end output with Tidy 10 array( 11 ’Output.TidyFormat’, 12 true 13 ), 14 // Assume XHTML 1.0 Strict Doctype 15 array( 16 ’HTML.Doctype’, 1 http://tools.ietf.org/html/rfc4287#section-8.1 461
  • 502. Zend Framework 2 Documentation, Release 2.3.1dev 17 ’XHTML 1.0 Strict’ 18 ), 19 // Disable cache, but see note after the example 20 array( 21 ’Cache.DefinitionImpl’, 22 null 23 ) 24 ); 25 26 // Configuring HTMLPurifier 27 $config = HTMLPurifier_Config::createDefault(); 28 foreach ($options as $option) { 29 $config->set($option[0], $option[1]); 30 } 31 32 // Creating a HTMLPurifier with it’s config 33 $purifier = new HTMLPurifier($config); 34 35 // Fetch the RSS 36 try { 37 $rss = ZendFeedReaderReader::import(’http://www.planet-php.net/rss/’); 38 } catch (ZendFeedExceptionReaderRuntimeException $e) { 39 // feed import failed 40 echo "Exception caught importing feed: {$e->getMessage()}n"; 41 exit; 42 } 43 44 // Initialize the channel data array 45 // See that we’re cleaning the description with HTMLPurifier 46 $channel = array( 47 ’title’ => $rss->getTitle(), 48 ’link’ => $rss->getLink(), 49 ’description’ => $purifier->purify($rss->getDescription()), 50 ’items’ => array() 51 ); 52 53 // Loop over each channel item and store relevant data 54 // See that we’re cleaning the descriptions with HTMLPurifier 55 foreach ($rss as $item) { 56 $channel[’items’][] = array( 57 ’title’ => $item->getTitle(), 58 ’link’ => $item->getLink(), 59 ’description’ => $purifier->purify($item->getDescription()) 60 ); 61 } 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. 462 Chapter 115. ZendFeed and Security
  • 503. Zend Framework 2 Documentation, Release 2.3.1dev 115.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 try { 2 $rss = ZendFeedReaderReader::import(’http://www.planet-php.net/rss/’); 3 } catch (ZendFeedExceptionReaderRuntimeException $e) { 4 // feed import failed 5 echo "Exception caught importing feed: {$e->getMessage()}n"; 6 exit; 7 } 8 9 // Validate all URIs 10 $linkValidator = new ZendValidatorUri; 11 $link = null; 12 if ($linkValidator->isValid($rss->getLink())) { 13 $link = $rss->getLink(); 14 } 15 16 // Escaper used for escaping data 17 $escaper = new ZendEscaperEscaper(’utf-8’); 18 19 // Initialize the channel data array 20 $channel = array( 21 ’title’ => $escaper->escapeHtml($rss->getTitle()), 22 ’link’ => $escaper->escapeHtml($link), 23 ’description’ => $escaper->escapeHtml($rss->getDescription()), 24 ’items’ => array() 25 ); 26 27 // Loop over each channel item and store relevant data 28 foreach ($rss as $item) { 29 $link = null; 30 if ($linkValidator->isValid($rss->getLink())) { 31 $link = $item->getLink(); 32 } 33 $channel[’items’][] = array( 34 ’title’ => $escaper->escapeHtml($item->getTitle()), 35 ’link’ => $escaper->escapeHtml($link), 36 ’description’ => $escaper->escapeHtml($item->getDescription()) 37 ); 38 } 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. 115.3. Escaping data using ZendEscaper 463
  • 504. Zend Framework 2 Documentation, Release 2.3.1dev 464 Chapter 115. ZendFeed and Security
  • 505. CHAPTER 116 ZendFeedReaderReader 116.1 Introduction ZendFeedReaderReader is a component used to consume RSS and Atom feeds of any version, includ- ing 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. 116.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. 1 $feed = ZendFeedReaderReader::import(’http://www.planet-php.net/rdf/’); 2 $data = array( 3 ’title’ => $feed->getTitle(), 465
  • 506. Zend Framework 2 Documentation, Release 2.3.1dev 4 ’link’ => $feed->getLink(), 5 ’dateModified’ => $feed->getDateModified(), 6 ’description’ => $feed->getDescription(), 7 ’language’ => $feed->getLanguage(), 8 ’entries’ => array(), 9 ); 10 11 foreach ($feed as $entry) { 12 $edata = array( 13 ’title’ => $entry->getTitle(), 14 ’description’ => $entry->getDescription(), 15 ’dateModified’ => $entry->getDateModified(), 16 ’authors’ => $entry->getAuthors(), 17 ’link’ => $entry->getLink(), 18 ’content’ => $entry->getContent() 19 ); 20 $data[’entries’][] = $edata; 21 } The example above demonstrates ZendFeedReaderReader‘s API, and it also demonstrates some of its inter- nal 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 // from a URI 2 $feed = ZendFeedReaderReader::import(’http://www.planet-php.net/rdf/’); 3 4 // from a String 5 $feed = ZendFeedReaderReader::importString($feedXmlString); 6 7 // from a file 8 $feed = ZendFeedReaderReader::importFile(’./feed.xml’); 116.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). • 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. 466 Chapter 116. ZendFeedReaderReader
  • 507. Zend Framework 2 Documentation, Release 2.3.1dev • 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 $feed = ZendFeedReaderReader::import(’http://www.planet-php.net/rdf/’); 2 $xpathPrefix = $feed->getXpathPrefix(); 3 $xpath = $feed->getXpath(); 4 $xpath->registerNamespace(’admin’, ’http://webns.net/mvcb/’); 5 $reportErrorsTo = $xpath->evaluate(’string(’ 6 . $xpathPrefix 7 . ’/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. 116.4 Cache Support and Intelligent Requests 116.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); 116.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); 4 ZendFeedReaderReader::useHttpConditionalGet(); 5 6 $feed = ZendFeedReaderReader::import(’http://www.planet-php.net/rdf/’); 116.4. Cache Support and Intelligent Requests 467
  • 508. Zend Framework 2 Documentation, Release 2.3.1dev In the example above, with HTTP Conditional GET requests enabled, the response header values for ETag and Last- Modified 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 $lastEtagReceived = ’5e6cefe7df5a7e95c8b1ba1a2ccaff3d’; 2 $lastModifiedDateReceived = ’Wed, 08 Jul 2009 13:37:22 GMT’; 3 $feed = ZendFeedReaderReader::import( 4 $uri, $lastEtagReceived, $lastModifiedDateReceived 5 ); 116.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 if (isset($links->rdf)) { 4 echo $links->rdf, "n"; // http://www.planet-php.org/rdf/ 5 } 6 if (isset($links->rss)) { 7 echo $links->rss, "n"; // http://www.planet-php.org/rss/ 8 } 9 if (isset($links->atom)) { 10 echo $links->atom, "n"; // http://www.planet-php.org/atom/ 11 } 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 foreach ($links as $link) { 4 echo $link[’href’], "n"; 5 } 468 Chapter 116. ZendFeedReaderReader
  • 509. Zend Framework 2 Documentation, Release 2.3.1dev 116.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 $feed = ZendFeedReaderReader::import(’http://www.example.com/atom.xml’); 2 $categories = $feed->getCategories(); 3 $labels = array(); 4 foreach ($categories as $cat) { 5 $labels[] = $cat[’label’] 6 } 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 $feed = ZendFeedReaderReader::import(’http://www.example.com/atom.xml’); 2 $categories = $feed->getCategories(); 3 $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. 116.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 116.6. Attribute Collections 469
  • 510. Zend Framework 2 Documentation, Release 2.3.1dev 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. 470 Chapter 116. ZendFeedReaderReader
  • 511. Zend Framework 2 Documentation, Release 2.3.1dev Table 116.1: Feed Level API Methods getId() Returns a unique ID associated with this feed getTitle() Returns the title of the feed getDescription() Returns the text description of the feed. getLink() 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). getFeedLink() 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. getAuthors() 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. getAu- thor(integer $index = 0) 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). getDateCre- ated() 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. getDateModi- fied() Returns the date on which this feed was last modified. The returned date will be a DateTime object. getLastBuild- Date() 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. getLanguage() Returns the language of the feed (if defined) or simply the language noted in the XML document. getGenerator() 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. getCopyright() Returns any copyright notice associated with the feed. getHubs() 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. getCategories() 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. getImage() 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 ele- ments 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 re- cent, 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. 116.7. Retrieving Feed Information 471
  • 512. Zend Framework 2 Documentation, Release 2.3.1dev 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 116.2: Extended Feed Level API Methods getDomDocu- ment() Returns the parent DOMDocument object for the entire source XML document getElement() Returns the current feed level DOMElement object saveXml() Returns a string containing an XML document of the entire feed element (this is not the original document but a rebuilt version) getXpath() Returns the DOMXPath object used internally to run queries on the DOMDocument object (this includes core and Extension namespaces pre-registered) getXpathPre- fix() Returns the valid DOM path prefix prepended to all XPath queries matching the feed being queried getEncoding() 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. count() Returns a count of the entries or items this feed contains (implements SPLCountable interface) current() Returns either the current entry (using the current index from key()) key() Returns the current entry index next() Increments the entry index value by one rewind() Resets the entry index to 0 valid() Checks that the current entry index is valid, i.e. it does fall below 0 and does not exceed the number of entries existing. getExten- sions() 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. getExten- sion(string $name) 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. getType() 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. 116.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. 472 Chapter 116. ZendFeedReaderReader
  • 513. Zend Framework 2 Documentation, Release 2.3.1dev Table 116.3: Entry Level API Methods getId() Returns a unique ID for the current entry. getTitle() Returns the title of the current entry. getDescription() Returns a description of the current entry. getLink() Returns a URI to the HTML version of the current entry. getPermaLink() Returns the permanent link to the current entry. In most cases, this is the same as using getLink(). getAuthors() 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. getAuthor(integer $index = 0) 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). getDateCreated() 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. getDateModified() Returns the date on which the current entry was last modified getContent() 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. getEnclosure() 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. getCommentCount() Returns the number of comments made on this entry at the time the feed was last generated getCommentLink() Returns a URI pointing to the HTML page where comments can be made on this entry getComment- FeedLink([string $type = ‘atom’|’rss’]) 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). getCategories() 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. 116.8. Retrieving Entry/Item Information 473
  • 514. Zend Framework 2 Documentation, Release 2.3.1dev 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 116.4: Extended Entry Level API Methods getDomDocu- ment() Returns the parent DOMDocument object for the entire feed (not just the current entry) getElement() Returns the current entry level DOMElement object getXpath() Returns the DOMXPath object used internally to run queries on the DOMDocument object (this includes core and Extension namespaces pre-registered) getXpathPre- fix() Returns the valid DOM path prefix prepended to all XPath queries matching the entry being queried getEncoding() 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. getExten- sions() 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. getExten- sion(string $name) 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. getType() 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. 116.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- dard’s name. For example, internally we have ZendFeedReaderExtensionDublinCoreFeed and 474 Chapter 116. ZendFeedReaderReader
  • 515. Zend Framework 2 Documentation, Release 2.3.1dev 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 116.5: Core Extensions (pre-registered) DublinCore (Feed and Entry) Implements support for Dublin Core Metadata Element Set 1.0 and 1.1 Content (Entry only) Implements support for Content 1.0 Atom (Feed and Entry) Implements support for Atom 0.3 and Atom 1.0 Slash Implements support for the Slash RSS 1.0 module WellFormedWeb Implements support for the Well Formed Web CommentAPI 1.0 Thread Implements support for Atom Threading Extensions as described in RFC 4685 Podcast 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 116.6: Non-Core Extensions (must register manually) Syndication Implements Syndication 1.0 support for RSS feeds CreativeCom- mons 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 ZendFeedReaderReader::registerExtension(’Syndication’); 2 $feed = ZendFeedReaderReader::import(’http://rss.slashdot.org/Slashdot/slashdot’); 3 $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 ZendFeedReaderReader::registerExtension(’Syndication’); 2 $feed = ZendFeedReaderReader::import(’http://rss.slashdot.org/Slashdot/slashdot’); 3 $syndication = $feed->getExtension(’Syndication’); 4 $updatePeriod = $syndication->getUpdatePeriod(); 116.9. Extending Feed and Entry APIs 475
  • 516. Zend Framework 2 Documentation, Release 2.3.1dev 116.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 JungleBooks 1.0: 2 http://example.com/junglebooks/rss/module/1.0/ A snippet of RSS containing this extension in practice could be something similar to: 1 <?xml version="1.0" encoding="utf-8" ?> 2 <rss version="2.0" 3 xmlns:content="http://purl.org/rss/1.0/modules/content/" 4 xmlns:jungle="http://example.com/junglebooks/rss/module/1.0/"> 5 <channel> 6 <title>Jungle Books Customer Reviews</title> 7 <link>http://example.com/junglebooks</link> 8 <description>Many book reviews!</description> 9 <pubDate>Fri, 26 Jun 2009 19:15:10 GMT</pubDate> 10 <jungle:dayPopular> 11 http://example.com/junglebooks/book/938 12 </jungle:dayPopular> 13 <item> 14 <title>Review Of Flatland: A Romance of Many Dimensions</title> 15 <link>http://example.com/junglebooks/review/987</link> 16 <author>Confused Physics Student</author> 17 <content:encoded> 18 A romantic square?! 19 </content:encoded> 20 <pubDate>Thu, 25 Jun 2009 20:03:28 -0700</pubDate> 21 <jungle:isbn>048627263X</jungle:isbn> 22 </item> 23 </channel> 24 </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 class MyFeedReaderExtensionJungleBooksEntry 2 extends ZendFeedReaderExtensionAbstractEntry 3 { 4 public function getIsbn() 5 { 6 if (isset($this->data[’isbn’])) { 7 return $this->data[’isbn’]; 8 } 9 $isbn = $this->xpath->evaluate( 10 ’string(’ . $this->getXpathPrefix() . ’/jungle:isbn)’ 11 ); 12 if (!$isbn) { 13 $isbn = null; 476 Chapter 116. ZendFeedReaderReader
  • 517. Zend Framework 2 Documentation, Release 2.3.1dev 14 } 15 $this->data[’isbn’] = $isbn; 16 return $this->data[’isbn’]; 17 } 18 19 protected function registerNamespaces() 20 { 21 $this->xpath->registerNamespace( 22 ’jungle’, ’http://example.com/junglebooks/rss/module/1.0/’ 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 if (!ZendFeedReaderReader::isRegistered(’JungleBooks’)) { 2 $extensions = ZendFeedReaderReader::getExtensionManager(); 3 $extensions->setInvokableClass(’JungleBooksEntry’, ’MyFeedReaderExtensionJungleBooksEntry’); 4 ZendFeedReaderReader::registerExtension(’JungleBooks’); 5 } 6 $feed = ZendFeedReaderReader::import(’http://example.com/junglebooks/rss’); 7 8 // ISBN for whatever book the first entry in the feed was concerned with 9 $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 class MyFeedReaderExtensionJungleBooksFeed 2 extends ZendFeedReaderExtensionAbstractFeed 3 { 4 public function getDaysPopularBookLink() 5 { 6 if (isset($this->data[’dayPopular’])) { 7 return $this->data[’dayPopular’]; 8 } 9 $dayPopular = $this->xpath->evaluate( 10 ’string(’ . $this->getXpathPrefix() . ’/jungle:dayPopular)’ 11 ); 12 if (!$dayPopular) { 13 $dayPopular = null; 14 } 15 $this->data[’dayPopular’] = $dayPopular; 16 return $this->data[’dayPopular’]; 17 } 18 116.9. Extending Feed and Entry APIs 477
  • 518. Zend Framework 2 Documentation, Release 2.3.1dev 19 protected function registerNamespaces() 20 { 21 $this->xpath->registerNamespace( 22 ’jungle’, ’http://example.com/junglebooks/rss/module/1.0/’ 23 ); 24 } 25 } Let’s repeat the last example using a custom Extension to show the method being used. 1 if (!ZendFeedReaderReader::isRegistered(’JungleBooks’)) { 2 $extensions = ZendFeedReaderReader::getExtensionManager(); 3 $extensions->setInvokableClass(’JungleBooksFeed’, ’MyFeedReaderExtensionJungleBooksFeed’); 4 ZendFeedReaderReader::registerExtension(’JungleBooks’); 5 } 6 $feed = ZendFeedReaderReader::import(’http://example.com/junglebooks/rss’); 7 8 // URI to the information page of the day’s most popular book with visitors 9 $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. 478 Chapter 116. ZendFeedReaderReader
  • 519. CHAPTER 117 ZendFeedWriterWriter 117.1 Introduction ZendFeedWriterWriter is the sibling component to ZendFeedReaderReader responsible for gen- erating 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. 117.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 the entry’s original source). The latter implements the Atom Tombstones RFC allowing feeds to carry references to entries which have been deleted. 479
  • 520. Zend Framework 2 Documentation, Release 2.3.1dev While there are two main data container types, there are four renderers - two matching container renderers per sup- ported 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. 117.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 * Create the parent feed 3 */ 4 $feed = new ZendFeedWriterFeed; 5 $feed->setTitle(’Paddy’s Blog’); 6 $feed->setLink(’http://www.example.com’); 7 $feed->setFeedLink(’http://www.example.com/atom’, ’atom’); 8 $feed->addAuthor(array( 9 ’name’ => ’Paddy’, 10 ’email’ => ’[email protected]’, 11 ’uri’ => ’http://www.example.com’, 12 )); 13 $feed->setDateModified(time()); 14 $feed->addHub(’http://pubsubhubbub.appspot.com/’); 15 16 /** 17 * Add one or more entries. Note that entries must 18 * be manually added once created. 19 */ 20 $entry = $feed->createEntry(); 21 $entry->setTitle(’All Your Base Are Belong To Us’); 22 $entry->setLink(’http://www.example.com/all-your-base-are-belong-to-us’); 23 $entry->addAuthor(array( 24 ’name’ => ’Paddy’, 25 ’email’ => ’[email protected]’, 26 ’uri’ => ’http://www.example.com’, 27 )); 28 $entry->setDateModified(time()); 29 $entry->setDateCreated(time()); 30 $entry->setDescription(’Exposing the difficultly of porting games to English.’); 31 $entry->setContent( 32 ’I am not writing the article. The example is long enough as is ;).’ 33 ); 480 Chapter 117. ZendFeedWriterWriter
  • 521. Zend Framework 2 Documentation, Release 2.3.1dev 34 $feed->addEntry($entry); 35 36 /** 37 * Render the resulting feed to Atom 1.0 and assign to $out. 38 * You can substitute "atom" with "rss" to generate an RSS 2.0 feed. 39 */ 40 $out = $feed->export(’atom’); The output rendered should be as follows: 1 <?xml version="1.0" encoding="utf-8"?> 2 <feed xmlns="http://www.w3.org/2005/Atom"> 3 <title type="text">Paddy’s Blog</title> 4 <subtitle type="text">Writing about PC Games since 176 BC.</subtitle> 5 <updated>2009-12-14T20:28:18+00:00</updated> 6 <generator uri="http://framework.zend.com" version="1.10.0alpha"> 7 ZendFeedWriter 8 </generator> 9 <link rel="alternate" type="text/html" href="http://www.example.com"/> 10 <link rel="self" type="application/atom+xml" 11 href="http://www.example.com/atom"/> 12 <id>http://www.example.com</id> 13 <author> 14 <name>Paddy</name> 15 <email>[email protected]</email> 16 <uri>http://www.example.com</uri> 17 </author> 18 <link rel="hub" href="http://pubsubhubbub.appspot.com/"/> 19 <entry> 20 <title type="html"><![CDATA[All Your Base Are Belong To 21 Us]]></title> 22 <summary type="html"> 23 <![CDATA[Exposing the difficultly of porting games to 24 English.]]> 25 </summary> 26 <published>2009-12-14T20:28:18+00:00</published> 27 <updated>2009-12-14T20:28:18+00:00</updated> 28 <link rel="alternate" type="text/html" 29 href="http://www.example.com/all-your-base-are-belong-to-us"/> 30 <id>http://www.example.com/all-your-base-are-belong-to-us</id> 31 <author> 32 <name>Paddy</name> 33 <email>[email protected]</email> 34 <uri>http://www.example.com</uri> 35 </author> 36 <content type="html"> 37 <![CDATA[I am not writing the article. 38 The example is long enough as is ;).]]> 39 </content> 40 </entry> 41 </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 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. 117.3. Getting Started 481
  • 522. Zend Framework 2 Documentation, Release 2.3.1dev 117.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. 482 Chapter 117. ZendFeedWriterWriter
  • 523. Zend Framework 2 Documentation, Release 2.3.1dev Table 117.1: Feed Level API Methods setId() 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. setTitle() Set the title of the feed. setDe- scription() Set the text description of the feed. setLink() 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). set- FeedLinks() 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”. addAu- thors() 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. addAu- thor() Sets the data for a single author following the same array format as described above for a single sub-array. setDate- Created() 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. setDate- Modified() Sets the date on which this feed was last modified. The expected parameter may be a UNIX timestamp or a DateTime object. setLast- Build- Date() Sets the date on which this feed was last build. The expected parameter may be a UNIX timestamp or a DateTime object. This will only be rendered for RSS 2.0 feeds and is automatically rendered as the current date by default when not explicitly set. set- Language() Sets the language of the feed. This will be omitted unless set. setGenera- tor() Allows the setting of a generator. The parameter should be an array containing the keys “name”, “version” and “uri”. If omitted a default generator will be added referencing ZendFeedWriter, the current Zend Framework version and the Framework’s URI. setCopy- right() Sets a copyright notice associated with the feed. 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. addCate- gories() 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 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. createEn- try() Returns a new instance of ZendFeedWriterEntry. This is the Entry level data container. New entries are not automatically assigned to the current feed, so you must explicitly call addEntry() to add the entry for rendering. addEntry() Adds an instance of ZendFeedWriterEntry to the current feed container for rendering. create- Tomb- stone() Returns a new instance of ZendFeedWriterDeleted. This is the Atom 2.0 Tombstone level data container. New entries are not automatically assigned to the current feed, so you must explicitly call addTombstone() to add the deleted entry for rendering. 117.4. Setting Feed Data Points 483
  • 524. Zend Framework 2 Documentation, Release 2.3.1dev 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. 117.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. 484 Chapter 117. ZendFeedWriterWriter
  • 525. Zend Framework 2 Documentation, Release 2.3.1dev Table 117.2: Entry Level API Methods setId() 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. setTitle() Set the title of the entry. setDescrip- tion() Set the text description of the entry. setContent() Set the content of the entry. setLink() 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). set- FeedLinks() 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. addAuthors() 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. addAuthor() Sets the data for a single author following the same format as described above for a single sub-array. setDateCre- ated() 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. setDateModi- fied() 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. setCopy- right() Sets a copyright notice associated with the feed. setCate- gories() 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. setComment- Count() Sets the number of comments associated with this entry. Rendering differs between RSS and Atom 2.0 depending on the element or attribute needed. setCom- mentLink() Seta a link to a HTML page containing comments associated with this entry. setComment- FeedLink() 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”. setComment- FeedLinks() Same as setCommentFeedLink() except it accepts an array of arrays, where each subarray contains the expected parameters of setCommentFeedLink(). setEncoding() 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. 117.5. Setting Entry Data Points 485
  • 526. Zend Framework 2 Documentation, Release 2.3.1dev 486 Chapter 117. ZendFeedWriterWriter
  • 527. CHAPTER 118 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. 118.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, Feed- burner, and there are plugins available for Wordpress blogs. 118.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. 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 487
  • 528. Zend Framework 2 Documentation, Release 2.3.1dev 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 communi- cation 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 immedi- ately 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 addi- tion, 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. 118.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 $publisher = new ZendFeedPubSubHubbubPublisher; 2 $publisher->addHubUrls(array( 3 ’http://pubsubhubbub.appspot.com/’, 4 ’http://hubbub.example.com’, 5 )); 6 $publisher->addUpdatedTopicUrls(array( 7 ’http://www.example.net/rss’, 8 ’http://www.example.net/atom’, 9 )); 10 $publisher->notifyAll(); 11 12 if (!$publisher->isSuccess()) { 13 // check for errors 14 $errors = $publisher->getErrors(); 488 Chapter 118. ZendFeedPubSubHubbub
  • 529. Zend Framework 2 Documentation, Release 2.3.1dev 15 $failedHubs = array(); 16 foreach ($errors as $error) { 17 $failedHubs[] = $error[’hubUrl’]; 18 } 19 } 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. 118.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. 118.4.1 Subscribing and Unsubscribing ZendFeedPubSubHubbubSubscriber implements a full Pubsubhubbub Subscriber capable of subscrib- ing to, or unsubscribing from, any Topic via any Hub advertised by that Topic. It operates in conjunction with 118.4. ZendFeedPubSubHubbubSubscriber 489
  • 530. Zend Framework 2 Documentation, Release 2.3.1dev ZendFeedPubSubHubbubSubscriberCallback which accepts requests from a Hub to confirm all sub- scription 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 $subscriber = new ZendFeedPubSubHubbubSubscriber; 4 $subscriber->setStorage($storage); 5 $subscriber->addHubUrl(’http://hubbub.example.com’); 6 $subscriber->setTopicUrl(’http://www.example.net/rss.xml’); 7 $subscriber->setCallbackUrl(’http://www.mydomain.com/hubbub/callback’); 8 $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 ap- plication. 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 CREATE TABLE IF NOT EXISTS ‘subscription‘ ( 2 ‘id‘ varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT ’’, 3 ‘topic_url‘ varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 4 ‘hub_url‘ varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 5 ‘created_time‘ datetime DEFAULT NULL, 6 ‘lease_seconds‘ bigint(20) DEFAULT NULL, 7 ‘verify_token‘ varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 8 ‘secret‘ varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 9 ‘expiration_time‘ datetime DEFAULT NULL, 10 ‘subscription_state‘ varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL, 11 PRIMARY KEY (‘id‘) 12 ) 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): 490 Chapter 118. ZendFeedPubSubHubbub
  • 531. Zend Framework 2 Documentation, Release 2.3.1dev Table 118.1: Subscription request parameters Pa- ram- eter Value Explanation hub.callbackhttp://www.mydomain.com/hubbub/callback?xhub.subscription=5536df06b5dcb966edab3a4c4dThe 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 ZendPubSubHubbubSubscriberCall- back::setSubscriptionKey(). A detailed example is offered later. hub.lease_seconds2592000 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.modesubscribe 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.verifysync 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.verifyasync 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_token3065919804ab- caa7212ae89.879827871253878386 A verification token returned to the Subscriber by the 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. 118.4. ZendFeedPubSubHubbubSubscriber 491
  • 532. Zend Framework 2 Documentation, Release 2.3.1dev You can modify several of these parameters to indicate a different prefer- ence. For example, you can set a different lease seconds value using ZendFeedPubSubHubbubSubscriber::setLeaseSeconds() or show a preference for the async ver- ify mode by using setPreferredVerificationMode(ZendFeedPubSubHubbubPubSubHubbub::VERIFICATION_ However the Hubs retain the capability to enforce their own preferences and for this reason the component is deliber- ately 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 sup- ported by ZendFeedPubSubHubbub), you may indicate a specific preference using the setPreferredVerificationMode() method. In “sync” (synchronous) mode, the Hub attempts to con- firm 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. 118.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 $storage = new ZendFeedPubSubHubbubModelSubscription; 2 $callback = new ZendFeedPubSubHubbubSubscriberCallback; 3 $callback->setStorage($storage); 4 $callback->handle(); 5 $callback->sendResponse(); 6 7 /** 8 * Check if the callback resulting in the receipt of a feed update. 9 * Otherwise it was either a (un)sub verification request or invalid request. 10 * Typically we need do nothing other than add feed update handling - the rest 11 * is handled internally by the class. 12 */ 13 if ($callback->hasFeedUpdate()) { 14 $feedString = $callback->getFeedUpdate(); 15 /** 16 * Process the feed update asynchronously to avoid a Hub timeout. 492 Chapter 118. ZendFeedPubSubHubbub
  • 533. Zend Framework 2 Documentation, Release 2.3.1dev 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. 118.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 specifi- cation, 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 param- eter being the key value available from the Router. The example below demonstrates this using a Zend Framework controller. 1 use ZendMvcControllerAbstractActionController; 2 3 class CallbackController extends AbstractActionController 4 { 5 6 public function indexAction() 7 { 8 $storage = new ZendFeedPubSubHubbubModelSubscription; 9 $callback = new ZendFeedPubSubHubbubSubscriberCallback; 10 $callback->setStorage($storage); 11 /** 12 * Inject subscription key parsing from URL path using 13 * a parameter from Router. 14 */ 118.4. ZendFeedPubSubHubbubSubscriber 493
  • 534. Zend Framework 2 Documentation, Release 2.3.1dev 15 $subscriptionKey = $this->params()->fromRoute(’subkey’); 16 $callback->setSubscriptionKey($subscriptionKey); 17 $callback->handle(); 18 $callback->sendResponse(); 19 20 /** 21 * Check if the callback resulting in the receipt of a feed update. 22 * Otherwise it was either a (un)sub verification request or invalid 23 * request. Typically we need do nothing other than add feed update 24 * handling - the rest is handled internally by the class. 25 */ 26 if ($callback->hasFeedUpdate()) { 27 $feedString = $callback->getFeedUpdate(); 28 /** 29 * Process the feed update asynchronously to avoid a Hub timeout. 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 // Callback Route to enable appending a PuSH Subscription’s lookup key 2 $route = ZendMvcRouterHttpSegment::factory(array( 3 ’route’ => ’/callback/:subkey’, 4 ’constraints’ => array( 5 ’subkey’ => ’[a-z0-9]+’ 6 ), 7 ’defaults’ => array( 8 ’controller’ => ’application-index’, 9 ’action’ => ’index’ 10 ) 11 )); 494 Chapter 118. ZendFeedPubSubHubbub
  • 535. CHAPTER 119 ZendFileClassFileLocator 119.1 Overview TODO 119.2 Available Methods TODO 119.3 Examples TODO 495
  • 536. Zend Framework 2 Documentation, Release 2.3.1dev 496 Chapter 119. ZendFileClassFileLocator
  • 537. CHAPTER 120 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. 120.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 trans- formation 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 con- tain 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. 120.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 echo $htmlEntities->filter(’&’); // &amp; 4 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: 497
  • 538. Zend Framework 2 Documentation, Release 2.3.1dev 1 $strtolower = new ZendFilterStringToLower; 2 3 echo $strtolower(’I LOVE ZF2!’); // i love zf2! 4 $zf2love = $strtolower(’I LOVE ZF2!’); orphan 120.3 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 echo StaticFilter::execute(’"’, 2 ’HtmlEntities’, 3 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 $pluginManager = StaticFilter::getPluginManager()->setInvokableClass( 2 ’myNewFilter’, ’MyCustomFilterMyNewFilter’ 3 ); 4 5 StaticFilter::setPluginManager(new MyFilterPluginManager()); This is useful when adding custom filters to be used by the StaticFilter. 120.4 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 3 // Attach a filter 4 $filter = new ZendFilterWordUnderscoreToCamelCase(); 5 $filtered = $filter->filter($original); 6 7 // Use it’s opposite 8 $filter2 = new ZendFilterWordCamelCaseToUnderscore(); 9 $filtered = $filter2->filter($filtered) 498 Chapter 120. Introduction to ZendFilter
  • 539. Zend Framework 2 Documentation, Release 2.3.1dev 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. 120.4. Double filtering 499
  • 540. Zend Framework 2 Documentation, Release 2.3.1dev 500 Chapter 120. Introduction to ZendFilter
  • 541. CHAPTER 121 Standard Filter Classes Zend Framework comes with a standard set of filters, which are ready for you to use. orphan 121.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. 121.1.1 Supported Options 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() 121.1.2 Basic Usage 1 // Default settings, deny whitespace 2 $filter = new ZendI18nFilterAlnum(); 3 echo $filter->filter("This is (my) content: 123"); 4 // Returns "Thisismycontent123" 5 6 // First param in constructor is $allowWhiteSpace 7 $filter = new ZendI18nFilterAlnum(true); 8 echo $filter->filter("This is (my) content: 123"); 9 // Returns "This is my content 123" 501
  • 542. Zend Framework 2 Documentation, Release 2.3.1dev 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. orphan 121.2 Alpha The Alpha filter can be used to return only alphabetic characters in the unicode “letter” category. All other characters are suppressed. 121.2.1 Supported Options 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() 121.2.2 Basic Usage 1 // Default settings, deny whitespace 2 $filter = new ZendI18nFilterAlpha(); 3 echo $filter->filter("This is (my) content: 123"); 4 // Returns "Thisismycontent" 5 6 // Allow whitespace 7 $filter = new ZendI18nFilterAlpha(true); 8 echo $filter->filter("This is (my) content: 123"); 9 // 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. orphan 121.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. 502 Chapter 121. Standard Filter Classes
  • 543. Zend Framework 2 Documentation, Release 2.3.1dev 121.3.1 Supported Options There are no additional options for ZendFilterBaseName. 121.3.2 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‘. orphan 121.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. 121.4.1 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. 121.4.2 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 $filter = new ZendFilterBoolean(); 2 $value = ’’; 3 $result = $filter->filter($value); 4 // 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. 121.4. Boolean 503
  • 544. Zend Framework 2 Documentation, Release 2.3.1dev 121.4.3 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 // converts 0 to false 2 $filter = new ZendFilterBoolean(ZendFilterBoolean::INTEGER); 3 4 // converts 0 and ’0’ to false 5 $filter = new ZendFilterBoolean( 6 ZendFilterBoolean::INTEGER + ZendFilterBoolean::ZERO 7 ); 8 9 // converts 0 and ’0’ to false 10 $filter = new ZendFilterBoolean(array( 11 ’type’ => array( 12 ZendFilterBoolean::INTEGER, 13 ZendFilterBoolean::ZERO, 14 ), 15 )); 16 17 // converts 0 and ’0’ to false 18 $filter = new ZendFilterBoolean(array( 19 ’type’ => array( 20 ’integer’, 21 ’zero’, 22 ), 23 )); You can also give an instance of ZendConfigConfig to set the desired types. To set types after instantiation, use the setType() method. 504 Chapter 121. Standard Filter Classes
  • 545. Zend Framework 2 Documentation, Release 2.3.1dev 121.4.4 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 $filter = new ZendFilterBoolean(array( 2 ’type’ => ZendFilterBoolean::ALL, 3 ’locale’ => ’de’, 4 )); 5 6 // returns false 7 echo $filter->filter(’nein’); 8 9 $filter->setLocale(’en’); 10 11 // returns true 12 $filter->filter(’yes’); 121.4.5 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 121.1: Usage without casting Type True False ZendFilterBoolean::BOOLEAN TRUE FALSE ZendFilterBoolean::INTEGER 0 1 ZendFilterBoolean::FLOAT 0.0 1.0 ZendFilterBoolean::STRING “” ZendFilterBoolean::ZERO “0” “1” ZendFilterBoolean::EMPTY_ARRAY array() ZendFilterBoolean::NULL NULL ZendFilterBoolean::FALSE_STRING “false” (case independently) “true” (case independently) ZendFilterBoolean::YES localized “yes” (case independently) localized “no” (case independently) The following example shows the behaviour when changing the casting option: 1 $filter = new ZendFilterBoolean(array( 2 ’type’ => ZendFilterBoolean::ALL, 3 ’casting’ => false, 4 )); 5 6 // returns false 7 echo $filter->filter(0); 8 9 // returns true 10 echo $filter->filter(1); 11 12 // returns the value 13 echo $filter->filter(2); 121.4. Boolean 505
  • 546. Zend Framework 2 Documentation, Release 2.3.1dev orphan 121.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. 121.5.1 Supported Options The following options are supported for ZendFilterCallback: • callback: This sets the callback which should be used. • callback_params: This property sets the options which are used when the callback is processed. 121.5.2 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 print $filter->filter(’Hello!’); 4 // 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 // Our classdefinition 2 class MyClass 3 { 4 public function Reverse($param); 5 } 6 7 // The filter definition 8 $filter = new ZendFilterCallback(array(’MyClass’, ’Reverse’)); 9 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. 121.5.3 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 $filter = new ZendFilterCallback( 2 array( 3 ’callback’ => ’MyMethod’, 4 ’options’ => array(’key’ => ’param1’, ’key2’ => ’param2’) 5 ) 506 Chapter 121. Standard Filter Classes
  • 547. Zend Framework 2 Documentation, Release 2.3.1dev 6 ); 7 $filter->filter(array(’value’ => ’Hello’)); When you would call the above method definition manually it would look like this: 1 $value = MyMethod(’Hello’, ’param1’, ’param2’); orphan 121.6 Compress and Decompress These two filters are capable of compressing and decompressing strings, files, and directories. 121.6.1 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. 121.6.2 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 ap- proximately 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) 121.6.3 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’); 121.6. Compress and Decompress 507
  • 548. Zend Framework 2 Documentation, Release 2.3.1dev 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). 1 $filter = new ZendFilterCompress(array( 2 ’adapter’ => ’Bz2’, 3 ’options’ => array( 4 ’blocksize’ => 8, 5 ), 6 )); 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 $filter = new ZendFilterCompress(’Bz2’); 2 $compressed = $filter->filter(’Uncompressed string’); 3 // Returns the compressed string Decompression works the same way. 1 $filter = new ZendFilterDecompress(’Bz2’); 2 $compressed = $filter->filter(’Compressed string’); 3 // 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. 121.6.4 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 $filter = new ZendFilterCompress(array( 2 ’adapter’ => ’Bz2’, 3 ’options’ => array( 4 ’archive’ => ’filename.bz2’, 5 ), 6 )); 7 $compressed = $filter->filter(’Uncompressed string’); 8 // 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. 508 Chapter 121. Standard Filter Classes
  • 549. Zend Framework 2 Documentation, Release 2.3.1dev When you want to compress a file, then you must give the name of the file with its path. 1 $filter = new ZendFilterCompress(array( 2 ’adapter’ => ’Bz2’, 3 ’options’ => array( 4 ’archive’ => ’filename.bz2’ 5 ), 6 )); 7 $compressed = $filter->filter(’C:tempcompressme.txt’); 8 // 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 subdirec- tories will be compressed into the archive. 1 $filter = new ZendFilterCompress(array( 2 ’adapter’ => ’Bz2’, 3 ’options’ => array( 4 ’archive’ => ’filename.bz2’ 5 ), 6 )); 7 $compressed = $filter->filter(’C:tempsomedir’); 8 // 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. 121.6.5 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 $filter = new ZendFilterDecompress(’Bz2’); 2 $decompressed = $filter->filter(’filename.bz2’); 3 // 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 $filter = new ZendFilterDecompress(array( 2 ’adapter’ => ’Zip’, 3 ’options’ => array( 4 ’target’ => ’C:temp’, 5 ) 6 )); 7 $decompressed = $filter->filter(’filename.zip’); 8 // Returns true on success and decompresses the archive file 9 // 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. 121.6. Compress and Decompress 509
  • 550. Zend Framework 2 Documentation, Release 2.3.1dev 121.6.6 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. 121.6.7 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. 121.6.8 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. 510 Chapter 121. Standard Filter Classes
  • 551. Zend Framework 2 Documentation, Release 2.3.1dev 121.6.9 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. 121.6.10 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. 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. 121.6. Compress and Decompress 511
  • 552. Zend Framework 2 Documentation, Release 2.3.1dev 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. 121.6.11 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. orphan 121.7 Digits Returns the string $value, removing all but digits. 121.7.1 Supported Options There are no additional options for ZendFilterDigits. 121.7.2 Basic Usage A basic example of usage is below: 1 $filter = new ZendFilterDigits(); 2 3 print $filter->filter(’October 2012’); This returns “2012”. 1 $filter = new ZendFilterDigits(); 2 3 print $filter->filter(’HTML 5 for Dummies’); 512 Chapter 121. Standard Filter Classes
  • 553. Zend Framework 2 Documentation, Release 2.3.1dev This returns “5”. orphan 121.8 Dir Given a string containing a path to a file, this function will return the name of the directory. 121.8.1 Supported Options There are no additional options for ZendFilterDir. 121.8.2 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”. orphan 121.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. 121.9.1 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. 121.8. Dir 513
  • 554. Zend Framework 2 Documentation, Release 2.3.1dev • 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. 121.9.2 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 // Use the BlockCipher adapter 2 $filter1 = new ZendFilterEncrypt(array(’adapter’ => ’BlockCipher’)); 3 4 // Use the OpenSSL adapter 5 $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 // Use the OpenSSL adapter 2 $filter = new ZendFilterEncrypt(); 3 $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. 121.9.3 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 // Use the default AES encryption algorithm 2 $filter = new ZendFilterEncrypt(array(’adapter’ => ’BlockCipher’)); 3 $filter->setKey(’encryption key’); 4 5 // or 6 // $filter = new ZendFilterEncrypt(array( 7 // ’adapter’ => ’BlockCipher’, 8 // ’key’ => ’encryption key’ 9 // )); 10 514 Chapter 121. Standard Filter Classes
  • 555. Zend Framework 2 Documentation, Release 2.3.1dev 11 $encrypted = $filter->filter(’text to be encrypted’); 12 printf ("Encrypted text: %sn", $encrypted); You can get and set the encryption values also afterwards with the getEncryption() and setEncryption() methods. 1 // Use the default AES encryption algorithm 2 $filter = new ZendFilterEncrypt(array(’adapter’ => ’BlockCipher’)); 3 $filter->setKey(’encryption key’); 4 var_dump($filter->getEncryption()); 5 6 // Will print: 7 //array(4) { 8 // ["key_iteration"]=> 9 // int(5000) 10 // ["algorithm"]=> 11 // string(3) "aes" 12 // ["hash"]=> 13 // string(6) "sha256" 14 // ["key"]=> 15 // string(14) "encryption key" 16 //} 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 $key = ’encryption key’; 2 $text = ’message to encrypt’; 3 4 // use the default adapter that is BlockCipher 5 $filter = new ZendFilterEncrypt(); 6 $filter->setKey(’encryption key’); 7 for ($i=0; $i < 10; $i++) { 8 printf("%d) %sn", $i, $filter->filter($text)); 9 } 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 // use the default adapter that is BlockCipher 2 $filter = new ZendFilterEncrypt(); 3 $filter->setKey(’encryption key’); 4 $filter->setVector(’12345678901234567890’); 5 printf("%sn", $filter->filter(’message’)); 6 7 // output: 8 // 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. 121.9. Encrypt and Decrypt 515
  • 556. Zend Framework 2 Documentation, Release 2.3.1dev 121.9.4 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 $content = ’04636a6cb8276fad0787a2e187803b6557f77825d5ca6ed4392be702b9754bb3MTIzNDU2Nzg5MDEyMzQ1NgZ+z 2 // use the default adapter that is BlockCipher 3 $filter = new ZendFilterDecrypt(); 4 $filter->setKey(’encryption key’); 5 printf("Decrypt: %sn", $filter->filter($content)); 6 7 // output: 8 // 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(). 121.9.5 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 // Use openssl and provide a private key 2 $filter = new ZendFilterEncrypt(array( 3 ’adapter’ => ’openssl’, 4 ’private’ => ’/path/to/mykey/private.pem’ 5 )); 6 7 // of course you can also give the public keys at initiation 8 $filter->setPublicKey(array( 9 ’/public/key/path/first.pem’, 10 ’/public/key/path/second.pem’ 11 )); 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 // Use openssl and provide a private key 2 $filter = new ZendFilterEncrypt(array( 3 ’adapter’ => ’openssl’, 4 ’private’ => ’/path/to/mykey/private.pem’ 5 )); 6 7 // of course you can also give the public keys at initiation 8 $filter->setPublicKey(array( 516 Chapter 121. Standard Filter Classes
  • 557. Zend Framework 2 Documentation, Release 2.3.1dev 9 ’/public/key/path/first.pem’, 10 ’/public/key/path/second.pem’ 11 )); 12 $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 // Use openssl and provide a private key 2 $filter = new ZendFilterEncrypt(array( 3 ’adapter’ => ’openssl’, 4 ’private’ => ’/path/to/mykey/private.pem’ 5 )); 6 7 // of course you can also give the public keys at initiation 8 $filter->setPublicKey(array( 9 ’/public/key/path/first.pem’, 10 ’/public/key/path/second.pem’ 11 )); 12 $filter->setPassphrase(’mypassphrase’); 13 14 $encrypted = $filter->filter(’text_to_be_encoded’); 15 $envelope = $filter->getEnvelopeKey(); 16 print $encrypted; 17 18 // For decryption look at the Decrypt filter 121.9.6 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 // Use openssl and provide a private key 2 $filter = new ZendFilterEncrypt(array( 3 ’adapter’ => ’openssl’, 4 ’private’ => ’/path/to/mykey/private.pem’, 5 ’public’ => ’/public/key/path/public.pem’, 6 ’package’ => true 7 )); 8 9 $encrypted = $filter->filter(’text_to_be_encoded’); 10 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 com- pression. But, and this is the negative aspect of this feature, the encrypted value can now only be decrypted by using ZendFilterEncrypt. 121.9. Encrypt and Decrypt 517
  • 558. Zend Framework 2 Documentation, Release 2.3.1dev 121.9.7 Compressing Content Based on the original value, the encrypted value can be a very large string. To reduce the value ZendFilterEncrypt allows the usage of compression. 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 // Use basic compression adapter 2 $filter1 = new ZendFilterEncrypt(array( 3 ’adapter’ => ’openssl’, 4 ’private’ => ’/path/to/mykey/private.pem’, 5 ’public’ => ’/public/key/path/public.pem’, 6 ’package’ => true, 7 ’compression’ => ’bz2’ 8 )); 9 10 // Use basic compression adapter 11 $filter2 = new ZendFilterEncrypt(array( 12 ’adapter’ => ’openssl’, 13 ’private’ => ’/path/to/mykey/private.pem’, 14 ’public’ => ’/public/key/path/public.pem’, 15 ’package’ => true, 16 ’compression’ => array(’adapter’ => ’zip’, ’target’ => ’usrtmptmp.zip’) 17 )); 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. 121.9.8 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 // Use openssl and provide a private key 2 $filter = new ZendFilterDecrypt(array( 3 ’adapter’ => ’openssl’, 4 ’private’ => ’/path/to/mykey/private.pem’ 5 )); 6 7 // of course you can also give the envelope keys at initiation 8 $filter->setEnvelopeKey(array( 9 ’/key/from/encoder/first.pem’, 10 ’/key/from/encoder/second.pem’ 11 )); 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 // Use openssl and provide a private key 2 $filter = new ZendFilterDecrypt(array( 3 ’adapter’ => ’openssl’, 518 Chapter 121. Standard Filter Classes
  • 559. Zend Framework 2 Documentation, Release 2.3.1dev 4 ’private’ => ’/path/to/mykey/private.pem’ 5 )); 6 7 // of course you can also give the envelope keys at initiation 8 $filter->setEnvelopeKey(array( 9 ’/key/from/encoder/first.pem’, 10 ’/key/from/encoder/second.pem’ 11 )); 12 $filter->setPassphrase(’mypassphrase’); At last, decode the content. Our complete example for decrypting the previously encrypted content looks like this. 1 // Use openssl and provide a private key 2 $filter = new ZendFilterDecrypt(array( 3 ’adapter’ => ’openssl’, 4 ’private’ => ’/path/to/mykey/private.pem’ 5 )); 6 7 // of course you can also give the envelope keys at initiation 8 $filter->setEnvelopeKey(array( 9 ’/key/from/encoder/first.pem’, 10 ’/key/from/encoder/second.pem’ 11 )); 12 $filter->setPassphrase(’mypassphrase’); 13 14 $decrypted = $filter->filter(’encoded_text_normally_unreadable’); 15 print $decrypted; orphan 121.10 HtmlEntities Returns the string $value, converting characters to their corresponding HTML entity equivalents where they exist. 121.10.1 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. 121.10. HtmlEntities 519
  • 560. Zend Framework 2 Documentation, Release 2.3.1dev 121.10.2 Basic Usage See the following example for the default behavior of this filter. 1 $filter = new ZendFilterHtmlEntities(); 2 3 print $filter->filter(’<’); 121.10.3 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 $input = "A ’single’ and " . ’"double"’; 4 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 $input = "A ’single’ and " . ’"double"’; 4 print $filter->filter($input); The above example returns A ’single’ and &quot;double&quot;. Notice that "double" quotes are fil- tered while ’single’ quotes are not altered. 1 $filter = new ZendFilterHtmlEntities(array(’quotestyle’ => ENT_NOQUOTES)); 2 3 $input = "A ’single’ and " . ’"double"’; 4 print $filter->filter($input); The above example returns A ’single’ and "double". Notice that neither "double" or ’single’ quotes are altered. 121.10.4 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 $filter->setQuoteStyle(ENT_QUOTES); 4 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 $filter->setQuoteStyle(ENT_QUOTES); 4 print $filter->getQuoteStyle(ENT_QUOTES); 520 Chapter 121. Standard Filter Classes
  • 561. Zend Framework 2 Documentation, Release 2.3.1dev 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 $filter->setQuoteStyle(ENT_QUOTES); 4 print $filter->getQuoteStyle(ENT_QUOTES); orphan 121.11 Int ZendFilterInt allows you to transform a scalar value which contains into an integer. 121.11.1 Supported Options There are no additional options for ZendFilterInt. 121.11.2 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’. orphan 121.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. 121.12.1 Supported Options The following options are supported for ZendFilterNull: • type: The variable type which should be supported. 121.12.2 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. 121.11. Int 521
  • 562. Zend Framework 2 Documentation, Release 2.3.1dev 1 $filter = new ZendFilterNull(); 2 $value = ’’; 3 $result = $filter->filter($value); 4 // returns null instead of the empty string 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. 121.12.3 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 // converts false to null 2 $filter = new ZendFilterNull(ZendFilterNull::BOOLEAN); 3 4 // converts false and 0 to null 5 $filter = new ZendFilterNull( 6 ZendFilterNull::BOOLEAN + ZendFilterNull::INTEGER 7 ); 8 9 // converts false and 0 to null 10 $filter = new ZendFilterNull( array( 11 ZendFilterNull::BOOLEAN, 12 ZendFilterNull::INTEGER 13 )); 14 15 // converts false and 0 to null 16 $filter = new ZendFilterNull(array( 17 ’boolean’, 18 ’integer’, 19 )); You can also give a Traversable or an array to set the wished types. To set types afterwards use setType(). orphan 522 Chapter 121. Standard Filter Classes
  • 563. Zend Framework 2 Documentation, Release 2.3.1dev 121.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 ex- tension (Intl). 121.13.1 Supported Options 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. If unset, it will use NumberFormatter::DEFAULT_STYLE as the default style. 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() 121.13.2 Basic Usage 1 $filter = new ZendI18nFilterNumberFormat("de_DE"); 2 echo $filter->filter(1234567.8912346); 3 // Returns "1.234.567,891" 4 5 $filter = new ZendI18nFilterNumberFormat("en_US", NumberFormatter::PERCENT); 6 echo $filter->filter(0.80); 7 // Returns "80%" 8 9 $filter = new ZendI18nFilterNumberFormat("fr_FR", NumberFormatter::SCIENTIFIC); 10 echo $filter->filter(0.00123456789); 11 // Returns "1,23456789E-3" orphan 121.14 PregReplace ZendFilterPregReplace performs a search using regular expressions and replaces all found elements. 121.14.1 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. 121.13. NumberFormat 523
  • 564. Zend Framework 2 Documentation, Release 2.3.1dev 121.14.2 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. 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 $filter = new ZendFilterPregReplace(array( 2 ’pattern’ => ’/bob/’, 3 ’replacement’ => ’john’, 4 )); 5 $input = ’Hi bob!’; 6 7 $filter->filter($input); 8 // 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 $filter = new ZendFilterPregReplace(); 2 $filter->setMatchPattern(array(’bob’, ’Hi’)) 3 ->setReplacement(array(’john’, ’Bye’)); 4 $input = ’Hi bob!’; 5 6 $filter->filter($input); 7 // returns ’Bye john!’ For a more complex usage take a look into PHP‘s PCRE Pattern Chapter. orphan 121.15 RealPath This filter will resolve given links and pathnames and returns canonicalized absolute pathnames. 121.15.1 Supported Options The following options are supported for ZendFilterRealPath: • exists: This option defaults to TRUE which checks if the given path really exists. 121.15.2 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. 524 Chapter 121. Standard Filter Classes
  • 565. Zend Framework 2 Documentation, Release 2.3.1dev 1 $filter = new ZendFilterRealPath(); 2 $path = ’/www/var/path/../../mypath’; 3 $filtered = $filter->filter($path); 4 5 // returns ’/www/mypath’ 121.15.3 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. 1 $filter = new ZendFilterRealPath(false); 2 $path = ’/www/var/path/../../non/existing/path’; 3 $filtered = $filter->filter($path); 4 5 // returns ’/www/non/existing/path’ 6 // even when file_exists or realpath would return false orphan 121.16 StringToLower This filter converts any input to be lowercased. 121.16.1 Supported Options The following options are supported for ZendFilterStringToLower: • encoding: This option can be used to set an encoding which has to be used. 121.16.2 Basic Usage This is a basic example: 1 $filter = new ZendFilterStringToLower(); 2 3 print $filter->filter(’SAMPLE’); 4 // returns "sample" 121.16.3 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 // using UTF-8 2 $filter = new ZendFilterStringToLower(’UTF-8’); 3 4 // or give an array which can be useful when using a configuration 5 $filter = new ZendFilterStringToLower(array(’encoding’ => ’UTF-8’)); 121.16. StringToLower 525
  • 566. Zend Framework 2 Documentation, Release 2.3.1dev 6 7 // or do this afterwards 8 $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. orphan 121.17 StringToUpper This filter converts any input to be uppercased. 121.17.1 Supported Options The following options are supported for ZendFilterStringToUpper: • encoding: This option can be used to set an encoding which has to be used. 121.17.2 Basic Usage This is a basic example for using the StringToUpper filter: 1 $filter = new ZendFilterStringToUpper(); 2 3 print $filter->filter(’Sample’); 4 // returns "SAMPLE" 121.17.3 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 // or do this afterwards 4 $filter->setEncoding(’ISO-8859-1’); orphan 121.18 StringTrim This filter modifies a given string such that certain characters are removed from the beginning and end. 526 Chapter 121. Standard Filter Classes
  • 567. Zend Framework 2 Documentation, Release 2.3.1dev 121.18.1 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. 121.18.2 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. 121.18.3 Default Behavior 1 $filter = new ZendFilterStringTrim(’:’); 2 // 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. orphan 121.19 StripNewLines This filter modifies a given string and removes all new line characters within that string. 121.19.1 Supported Options There are no additional options for ZendFilterStripNewLines: 121.19.2 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. orphan 121.19. StripNewLines 527
  • 568. Zend Framework 2 Documentation, Release 2.3.1dev 121.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. 121.20.1 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 from the given content. 121.20.2 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. 121.20.3 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 $input = "A text with <br/> a <a href=’link.com’>link</a>"; 4 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. 528 Chapter 121. Standard Filter Classes
  • 569. Zend Framework 2 Documentation, Release 2.3.1dev 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. 121.20.4 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 $input = "A text with <br/> a <img src=’picture.com’ width=’100’>picture</img>"; 4 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. orphan 121.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. 121.21.1 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. 121.21.2 Basic Usage See the following example for the default behaviour of this filter: 1 $filter = new ZendFilterUriNormalize(array( 2 ’enforcedScheme’ => ’https’ 3 )); 4 5 echo $filter->filter(’www.example.com’); As the result the string https://www.example.com will be output. 121.21. UriNormalize 529
  • 570. Zend Framework 2 Documentation, Release 2.3.1dev 530 Chapter 121. Standard Filter Classes
  • 571. CHAPTER 122 Word Filters In addition to the standard set of filters, there are several classes specific to filtering word strings. orphan 122.1 CamelCaseToDash This filter modifies a given string such that ‘CamelCaseWords’ are converted to ‘Camel-Case-Words’. 122.1.1 Supported Options There are no additional options for ZendFilterWordCamelCaseToDash: 122.1.2 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’. orphan 122.2 CamelCaseToSeparator This filter modifies a given string such that ‘CamelCaseWords’ are converted to ‘Camel Case Words’. 122.2.1 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. 531
  • 572. Zend Framework 2 Documentation, Release 2.3.1dev 122.2.2 Basic Usage A basic example of usage is below: 1 $filter = new ZendFilterWordCamelCaseToSeparator(’:’); 2 // or new ZendFilterWordCamelCaseToSeparator(array(’separator’ => ’:’)); 3 4 print $filter->filter(’ThisIsMyContent’); The above example returns ‘This:Is:My:Content’. 122.2.3 Default Behavior 1 $filter = new ZendFilterWordCamelCaseToSeparator(); 2 3 print $filter->filter(’ThisIsMyContent’); The above example returns ‘This Is My Content’. orphan 122.3 CamelCaseToUnderscore This filter modifies a given string such that ‘CamelCaseWords’ are converted to ‘Camel_Case_Words’. 122.3.1 Supported Options There are no additional options for ZendFilterWordCamelCaseToUnderscore: 122.3.2 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’. orphan 122.4 DashToCamelCase This filter modifies a given string such that ‘words-with-dashes’ are converted to ‘WordsWithDashes’. 122.4.1 Supported Options There are no additional options for ZendFilterWordDashToCamelCase: 532 Chapter 122. Word Filters
  • 573. Zend Framework 2 Documentation, Release 2.3.1dev 122.4.2 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’. orphan 122.5 DashToSeparator This filter modifies a given string such that ‘words-with-dashes’ are converted to ‘words with dashes’. 122.5.1 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. 122.5.2 Basic Usage A basic example of usage is below: 1 $filter = new ZendFilterWordDashToSeparator(’+’); 2 // or new ZendFilterWordCamelCaseToSeparator(array(’separator’ => ’+’)); 3 4 print $filter->filter(’this-is-my-content’); The above example returns ‘this+is+my+content’. 122.5.3 Default Behavior 1 $filter = new ZendFilterWordDashToSeparator(); 2 3 print $filter->filter(’this-is-my-content’); The above example returns ‘this is my content’. orphan 122.6 DashToUnderscore This filter modifies a given string such that ‘words-with-dashes’ are converted to ‘words_with_dashes’. 122.6.1 Supported Options There are no additional options for ZendFilterWordDashToUnderscore: 122.5. DashToSeparator 533
  • 574. Zend Framework 2 Documentation, Release 2.3.1dev 122.6.2 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’. orphan 122.7 SeparatorToCamelCase This filter modifies a given string such that ‘words with separators’ are converted to ‘WordsWithSeparators’. 122.7.1 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. 122.7.2 Basic Usage A basic example of usage is below: 1 $filter = new ZendFilterWordSeparatorToCamelCase(’:’); 2 // or new ZendFilterWordSeparatorToCamelCase(array(’separator’ => ’:’)); 3 4 print $filter->filter(’this:is:my:content’); The above example returns ‘ThisIsMyContent’. 122.7.3 Default Behavior 1 $filter = new ZendFilterWordSeparatorToCamelCase(); 2 3 print $filter->filter(’this is my content’); The above example returns ‘ThisIsMyContent’. orphan 122.8 SeparatorToDash This filter modifies a given string such that ‘words with separators’ are converted to ‘words-with-separators’. 534 Chapter 122. Word Filters
  • 575. Zend Framework 2 Documentation, Release 2.3.1dev 122.8.1 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. 122.8.2 Basic Usage A basic example of usage is below: 1 $filter = new ZendFilterWordSeparatorToDash(’:’); 2 // or new ZendFilterWordSeparatorToDash(array(’separator’ => ’:’)); 3 4 print $filter->filter(’this:is:my:content’); The above example returns ‘this-is-my-content’. 122.8.3 Default Behavior 1 $filter = new ZendFilterWordSeparatorToDash(); 2 3 print $filter->filter(’this is my content’); The above example returns ‘this-is-my-content’. orphan 122.9 SeparatorToSeparator This filter modifies a given string such that ‘words with separators’ are converted to ‘words-with-separators’. 122.9.1 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. 122.9.2 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’. 122.9. SeparatorToSeparator 535
  • 576. Zend Framework 2 Documentation, Release 2.3.1dev 122.9.3 Default Behaviour 1 $filter = new ZendFilterWordSeparatorToSeparator(); 2 3 print $filter->filter(’this is my content’); The above example returns ‘this-is-my-content’. orphan 122.10 UnderscoreToCamelCase This filter modifies a given string such that ‘words_with_underscores’ are converted to ‘WordsWithUnderscores’. 122.10.1 Supported Options There are no additional options for ZendFilterWordUnderscoreToCamelCase: 122.10.2 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’. orphan 122.11 UnderscoreToSeparator This filter modifies a given string such that ‘words_with_underscores’ are converted to ‘words with underscores’. 122.11.1 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. 122.11.2 Basic usage A basic example of usage is below: 1 $filter = new ZendFilterWordUnderscoreToSeparator(’+’); 2 // or new ZendFilterWordCamelCaseToSeparator(array(’separator’ => ’+’)); 3 4 print $filter->filter(’this_is_my_content’); The above example returns ‘this+is+my+content’. 536 Chapter 122. Word Filters
  • 577. Zend Framework 2 Documentation, Release 2.3.1dev 122.11.3 Default Behavior 1 $filter = new ZendFilterWordUnderscoreToSeparator(); 2 3 print $filter->filter(’this_is_my_content’); The above example returns ‘this is my content’. orphan 122.12 UnderscoreToDash This filter modifies a given string such that ‘words_with_underscores’ are converted to ‘words-with-underscores’. 122.12.1 Supported Options There are no additional options for ZendFilterWordUnderscoreToDash: 122.12.2 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’. 122.12. UnderscoreToDash 537
  • 578. Zend Framework 2 Documentation, Release 2.3.1dev 538 Chapter 122. Word Filters
  • 579. CHAPTER 123 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. orphan 123.1 Decrypt TODO orphan 123.2 Encrypt TODO orphan 123.3 Lowercase TODO orphan 123.4 Rename ZendFilterFileRename can be used to rename a file and/or move a file to a new path. 539
  • 580. Zend Framework 2 Documentation, Release 2.3.1dev 123.4.1 Supported Options The following set of options are supported: • target (string) default: "*" Target filename or directory, the new name of the source file. • 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 target path, a ZendFilterExceptionRuntimeException will be thrown. • randomize (boolean) default: false Shall target files have a random postfix attached? The ran- dom 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. 123.4.2 Usage Examples Move all filtered files to a different directory: 1 // ’target’ option is assumed if param is a string 2 $filter = new ZendFilterFileRename("/tmp/"); 3 echo $filter->filter("./myfile.txt"); 4 // File has been moved to "/tmp/myfile.txt" Rename all filtered files to a new name: 1 $filter = new ZendFilterFileRename("/tmp/newfile.txt"); 2 echo $filter->filter("./myfile.txt"); 3 // File has been renamed to "/tmp/newfile.txt" Move to a new path and randomize file names: 1 $filter = new ZendFilterFileRename(array( 2 "target" => "/tmp/newfile.txt", 3 "randomize" => true, 4 )); 5 echo $filter->filter("./myfile.txt"); 6 // File has been renamed to "/tmp/newfile_4b3403665fea6.txt" Configure different options for several possible source files: 1 $filter = new ZendFilterFileRename(array( 2 array( 3 "source" => "fileA.txt" 4 "target" => "/dest1/newfileA.txt", 5 "overwrite" => true, 6 ), 7 array( 8 "source" => "fileB.txt" 9 "target" => "/dest2/newfileB.txt", 10 "randomize" => true, 11 ), 12 )); 540 Chapter 123. File Filter Classes
  • 581. Zend Framework 2 Documentation, Release 2.3.1dev 13 echo $filter->filter("fileA.txt"); 14 // File has been renamed to "/dest1/newfileA.txt" 15 echo $filter->filter("fileB.txt"); 16 // File has been renamed to "/dest2/newfileB_4b3403665fea6.txt" 123.4.3 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. orphan 123.5 RenameUpload ZendFilterFileRenameUpload can be used to rename or move an uploaded file to a new path. 123.5.1 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 target path, a ZendFilterExceptionRuntimeException will be thrown. • randomize (boolean) default: false Shall target files have a random postfix attached? The ran- dom 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: false When true, the uploaded file will maintains its original extension if not specified. For example, if the uploaded file is "file.txt" and the target is something like "mynewfile", the upload will be renamed to "mynewfile.txt". 123.5. RenameUpload 541
  • 582. Zend Framework 2 Documentation, Release 2.3.1dev 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. 123.5.2 Usage Examples Move all filtered files to a different directory: 1 use ZendHttpPhpEnvironmentRequest; 2 3 $request = new Request(); 4 $files = $request->getFiles(); 5 // i.e. $files[’my-upload’][’tmp_name’] === ’/tmp/php5Wx0aJ’ 6 // i.e. $files[’my-upload’][’name’] === ’myfile.txt’ 7 8 // ’target’ option is assumed if param is a string 9 $filter = new ZendFilterFileRenameUpload("./data/uploads/"); 10 echo $filter->filter($files[’my-upload’]); 11 // File has been moved to "./data/uploads/php5Wx0aJ" 12 13 // ... or retain the uploaded file name 14 $filter->setUseUploadName(true); 15 echo $filter->filter($files[’my-upload’]); 16 // File has been moved to "./data/uploads/myfile.txt" Rename all filtered files to a new name: 1 use ZendHttpPhpEnvironmentRequest; 2 3 $request = new Request(); 4 $files = $request->getFiles(); 5 // i.e. $files[’my-upload’][’tmp_name’] === ’/tmp/php5Wx0aJ’ 6 7 $filter = new ZendFilterFileRename("./data/uploads/newfile.txt"); 8 echo $filter->filter($files[’my-upload’]); 9 // File has been renamed to "./data/uploads/newfile.txt" Move to a new path and randomize file names: 1 use ZendHttpPhpEnvironmentRequest; 2 3 $request = new Request(); 4 $files = $request->getFiles(); 5 // i.e. $files[’my-upload’][’tmp_name’] === ’/tmp/php5Wx0aJ’ 6 7 $filter = new ZendFilterFileRename(array( 8 "target" => "./data/uploads/newfile.txt", 9 "randomize" => true, 10 )); 11 echo $filter->filter($files[’my-upload’]); 12 // File has been renamed to "./data/uploads/newfile_4b3403665fea6.txt" orphan 542 Chapter 123. File Filter Classes
  • 583. Zend Framework 2 Documentation, Release 2.3.1dev 123.6 Uppercase TODO 123.6. Uppercase 543
  • 584. Zend Framework 2 Documentation, Release 2.3.1dev 544 Chapter 123. File Filter Classes
  • 585. CHAPTER 124 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 // Create a filter chain and add filters to the chain 2 $filterChain = new ZendFilterFilterChain(); 3 $filterChain->attach(new ZendI18nFilterAlpha()) 4 ->attach(new ZendFilterStringToLower()); 5 6 // Filter the username 7 $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. 124.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 // Create a filter chain and add filters to the chain 2 $filterChain = new ZendFilterFilterChain(); 3 $filterChain->attach(new ZendI18nFilterAlpha()) 4 ->attach(new ZendFilterStringToLower(), 500); 124.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 filter within the FilterPluginManager. The second parameter takes any options for creating the filter instance. The third parameter is the priority. 545
  • 586. Zend Framework 2 Documentation, Release 2.3.1dev 1 // Create a filter chain and add filters to the chain 2 $filterChain = new ZendFilterFilterChain(); 3 $filterChain->attachByName(’alpha’) 4 ->attachByName(’stringtolower’, array(’encoding’ => ’utf-8’), 500); The following example shows how to add a custom filter to the FilterPluginManager and the FilterChain. 1 $filterChain = new ZendFilterFilterChain(); 2 $filterChain->getPluginManager()->setInvokableClass( 3 ’myNewFilter’, ’MyCustomFilterMyNewFilter’ 4 ); 5 $filterChain->attachByName(’alpha’) 6 ->attachByName(’myNewFilter’); You can also add your own FilterPluginManager implementation. 1 $filterChain = new ZendFilterFilterChain(); 2 $filterChain->setPluginManager(new MyFilterPluginManager()); 3 $filterChain->attach(new ZendI18nFilterAlpha()) 4 ->attach(new MyCustomFilterMyNewFilter()); 546 Chapter 124. Filter Chains
  • 587. CHAPTER 125 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 call- ing filter() on the object instance. 125.1 Transforming MixedCase and camelCaseText to another format 1 $inflector = new ZendFilterInflector(’pages/:page.:suffix’); 2 $inflector->setRules(array( 3 ’:page’ => array(’WordCamelCaseToDash’, ’StringToLower’), 4 ’suffix’ => ’html’, 5 )); 6 7 $string = ’camelCasedWords’; 8 $filtered = $inflector->filter(array(’page’ => $string)); 9 // pages/camel-cased-words.html 10 11 $string = ’this_is_not_camel_cased’; 12 $filtered = $inflector->filter(array(’page’ => $string)); 13 // pages/this_is_not_camel_cased.html 125.1.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. As an example, you can use any ZendFilter concrete implementations; however, instead of referring to them as ‘ZendI18nFilterAlpha‘ or ‘ZendFilterStringToLower‘, you’d specify only ‘Alpha‘ or ‘StringToLower‘. 547
  • 588. Zend Framework 2 Documentation, Release 2.3.1dev 125.1.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 // use ZendFilterWordCamelCaseToDash as a rule 2 $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 $filters = $inflector->getPluginManager(); 2 $filters->addInvokableClass(’mungify’, ’MyCustomFilterMungify’); 125.1.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 // Via constructor: 2 $inflector = new ZendFilterInflector(’#foo/#bar.#sfx’, null, ’#’); 3 4 // Via accessor: 5 $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 class Foo 2 { 3 /** 4 * @var string Inflector target 5 */ 6 protected $_target = ’foo/:bar/:baz.:suffix’; 7 8 /** 9 * Constructor 10 * @return void 11 */ 12 public function __construct() 13 { 14 $this->_inflector = new ZendFilterInflector(); 15 $this->_inflector->setTargetReference($this->_target); 16 } 17 18 /** 548 Chapter 125. ZendFilterInflector
  • 589. Zend Framework 2 Documentation, Release 2.3.1dev 19 * Set target; updates target in inflector 20 * 21 * @param string $target 22 * @return Foo 23 */ 24 public function setTarget($target) 25 { 26 $this->_target = $target; 27 return $this; 28 } 29 } 125.1.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-by- one, 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. 125.2 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 $inflector = new ZendFilterInflector(’:script.:suffix’); 2 $inflector->setStaticRule(’suffix’, ’phtml’); 3 4 // change it later: 5 $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 class Foo 2 { 3 /** 4 * @var string Suffix 5 */ 6 protected $_suffix = ’phtml’; 7 8 /** 9 * Constructor 10 * @return void 11 */ 12 public function __construct() 13 { 14 $this->_inflector = new ZendFilterInflector(’:script.:suffix’); 125.2. Static Rules 549
  • 590. Zend Framework 2 Documentation, Release 2.3.1dev 15 $this->_inflector->setStaticRuleReference(’suffix’, $this->_suffix); 16 } 17 18 /** 19 * Set suffix; updates suffix static rule in inflector 20 * 21 * @param string $suffix 22 * @return Foo 23 */ 24 public function setSuffix($suffix) 25 { 26 $this->_suffix = $suffix; 27 return $this; 28 } 29 } 125.3 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 // Set rule to use ZendFilterWordCamelCaseToDash filter 4 $inflector->setFilterRule(’script’, ’WordCamelCaseToDash’); 5 6 // Add rule to lowercase string 7 $inflector->addFilterRule(’script’, new ZendFilterStringToLower()); 8 9 // Set rules en-masse 10 $inflector->setFilterRule(’script’, array( 11 ’WordCamelCaseToDash’, 12 new ZendFilterStringToLower() 13 )); 125.4 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 550 Chapter 125. ZendFilterInflector
  • 591. Zend Framework 2 Documentation, Release 2.3.1dev to the following notation: • ‘:’ prefix: filter rules. • No prefix: static rule. Setting Multiple Rules at Once 1 // Could also use setRules() with this notation: 2 $inflector->addRules(array( 3 // filter rules: 4 ’:controller’ => array(’CamelCaseToUnderscore’,’StringToLower’), 5 ’:action’ => array(’CamelCaseToUnderscore’,’StringToLower’), 6 7 // Static rule: 8 ’suffix’ => ’phtml’ 9 )); 125.4.1 Utility Methods ZendFilterInflector has a number of utility methods for retrieving and setting the plugin loader, manipu- lating 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 excep- tion 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. 125.4.2 Using a Traversable or an array 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. • 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(). 125.4. Setting Many Rules At Once 551
  • 592. Zend Framework 2 Documentation, Release 2.3.1dev 125.5 Example 1 // With the constructor: 2 $options; // implements Traversable 3 $inflector = new ZendFilterInflector($options); 4 5 // Or with setOptions(): 6 $inflector = new ZendFilterInflector(); 7 $inflector->setOptions($options); 552 Chapter 125. ZendFilterInflector
  • 593. CHAPTER 126 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 echo StaticFilter::execute(’"’, 2 ’HtmlEntities’, 3 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 $pluginManager = StaticFilter::getPluginManager()->setInvokableClass( 2 ’myNewFilter’, ’MyCustomFilterMyNewFilter’ 3 ); 4 5 StaticFilter::setPluginManager(new MyFilterPluginManager()); This is useful when adding custom filters to be used by the StaticFilter. 553
  • 594. Zend Framework 2 Documentation, Release 2.3.1dev 554 Chapter 126. Using the StaticFilter
  • 595. CHAPTER 127 Writing Filters ZendFilter supplies a set of commonly needed filters, but developers will often need to write cus- tom 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. 127.1 Example The following example demonstrates how to write a custom filter: 1 namespace ApplicationFilter; 2 3 use ZendFilterFilterInterface; 4 5 class MyFilter implements FilterInterface 6 { 7 public function filter($value) 8 { 9 // perform some transformation upon $value to arrive on $valueFiltered 10 11 return $valueFiltered; 12 } 13 } To attach an instance of the filter defined above to a filter chain: 1 $filterChain = new ZendFilterFilterChain(); 2 $filterChain->attach(new ApplicationFilterMyFilter()); 555
  • 596. Zend Framework 2 Documentation, Release 2.3.1dev 556 Chapter 127. Writing Filters
  • 597. CHAPTER 128 Introduction 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 def- inition, 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. 557
  • 598. Zend Framework 2 Documentation, Release 2.3.1dev 558 Chapter 128. Introduction
  • 599. CHAPTER 129 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. 129.1 Programmatic Form Creation If nothing else, you can simply start creating elements, fieldsets, and forms and wiring them together. 1 use ZendCaptcha; 2 use ZendFormElement; 3 use ZendFormFieldset; 4 use ZendFormForm; 5 use ZendInputFilterInput; 6 use ZendInputFilterInputFilter; 7 8 $name = new Element(’name’); 9 $name->setLabel(’Your name’); 10 $name->setAttributes(array( 11 ’type’ => ’text’ 12 )); 13 14 $email = new ElementEmail(’email’); 15 $email->setLabel(’Your email address’); 16 17 $subject = new Element(’subject’); 18 $subject->setLabel(’Subject’); 19 $subject->setAttributes(array( 20 ’type’ => ’text’ 21 )); 22 23 $message = new ElementTextarea(’message’); 24 $message->setLabel(’Message’); 25 26 $captcha = new ElementCaptcha(’captcha’); 27 $captcha->setCaptcha(new CaptchaDumb()); 28 $captcha->setLabel(’Please verify you are human’); 559
  • 600. Zend Framework 2 Documentation, Release 2.3.1dev 29 30 $csrf = new ElementCsrf(’security’); 31 32 $send = new Element(’send’); 33 $send->setValue(’Submit’); 34 $send->setAttributes(array( 35 ’type’ => ’submit’ 36 )); 37 38 39 $form = new Form(’contact’); 40 $form->add($name); 41 $form->add($email); 42 $form->add($subject); 43 $form->add($message); 44 $form->add($captcha); 45 $form->add($csrf); 46 $form->add($send); 47 48 $nameInput = new Input(’name’); 49 // configure input... and all others 50 $inputFilter = new InputFilter(); 51 // 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 $sender = new Fieldset(’sender’); 2 $sender->add($name); 3 $sender->add($email); 4 5 $details = new Fieldset(’details’); 6 $details->add($subject); 7 $details->add($message); 8 9 $form = new Form(’contact’); 10 $form->add($sender); 11 $form->add($details); 12 $form->add($captcha); 13 $form->add($csrf); 14 $form->add($send); Regardless of approach, as you can see, this can be tedious. 129.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. 1 use ZendFormFactory; 2 3 $factory = new Factory(); 4 $form = $factory->createForm(array( 5 ’hydrator’ => ’ZendStdlibHydratorArraySerializable’, 6 ’elements’ => array( 560 Chapter 129. Quick Start
  • 601. Zend Framework 2 Documentation, Release 2.3.1dev 7 array( 8 ’spec’ => array( 9 ’name’ => ’name’, 10 ’options’ => array( 11 ’label’ => ’Your name’, 12 ), 13 ’type’ => ’Text’, 14 ) 15 ), 16 array( 17 ’spec’ => array( 18 ’type’ => ’ZendFormElementEmail’, 19 ’name’ => ’email’, 20 ’options’ => array( 21 ’label’ => ’Your email address’, 22 ) 23 ), 24 ), 25 array( 26 ’spec’ => array( 27 ’name’ => ’subject’, 28 ’options’ => array( 29 ’label’ => ’Subject’, 30 ), 31 ’type’ => ’Text’, 32 ), 33 ), 34 array( 35 ’spec’ => array( 36 ’type’ => ’ZendFormElementTextarea’, 37 ’name’ => ’message’, 38 ’options’ => array( 39 ’label’ => ’Message’, 40 ) 41 ), 42 ), 43 array( 44 ’spec’ => array( 45 ’type’ => ’ZendFormElementCaptcha’, 46 ’name’ => ’captcha’, 47 ’options’ => array( 48 ’label’ => ’Please verify you are human.’, 49 ’captcha’ => array( 50 ’class’ => ’Dumb’, 51 ), 52 ), 53 ), 54 ), 55 array( 56 ’spec’ => array( 57 ’type’ => ’ZendFormElementCsrf’, 58 ’name’ => ’security’, 59 ), 60 ), 61 array( 62 ’spec’ => array( 63 ’name’ => ’send’, 64 ’type’ => ’Submit’, 129.2. Creation via Factory 561
  • 602. Zend Framework 2 Documentation, Release 2.3.1dev 65 ’attributes’ => array( 66 ’value’ => ’Submit’, 67 ), 68 ), 69 ), 70 ), 71 /* If we had fieldsets, they’d go here; fieldsets contain 72 * "elements" and "fieldsets" keys, and potentially a "type" 73 * key indicating the specific FieldsetInterface 74 * implementation to use. 75 ’fieldsets’ => array( 76 ), 77 */ 78 79 // Configuration to pass on to 80 // ZendInputFilterFactory::createInputFilter() 81 ’input_filter’ => array( 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 $factory = new Factory(); 4 $form = $factory->createForm(array( 5 ’hydrator’ => ’ZendStdlibHydratorArraySerializable’, 6 ’fieldsets’ => array( 7 array( 8 ’spec’ => array( 9 ’name’ => ’sender’, 10 ’elements’ => array( 11 array( 12 ’spec’ => array( 13 ’name’ => ’name’, 14 ’options’ => array( 15 ’label’ => ’Your name’, 16 ), 17 ’type’ => ’Text’ 18 ), 19 ), 20 array( 21 ’spec’ => array( 22 ’type’ => ’ZendFormElementEmail’, 23 ’name’ => ’email’, 24 ’options’ => array( 25 ’label’ => ’Your email address’, 26 ), 27 ), 28 ), 29 ), 30 ), 31 ), 32 array( 33 ’spec’ => array( 34 ’name’ => ’details’, 35 ’elements’ => array( 36 array( 562 Chapter 129. Quick Start
  • 603. Zend Framework 2 Documentation, Release 2.3.1dev 37 ’spec’ => array( 38 ’name’ => ’subject’, 39 ’options’ => array( 40 ’label’ => ’Subject’, 41 ), 42 ’type’ => ’Text’, 43 ), 44 ), 45 array( 46 ’spec’ => array( 47 ’name’ => ’message’, 48 ’type’ => ’ZendFormElementTextarea’, 49 ’options’ => array( 50 ’label’ => ’Message’, 51 ), 52 ), 53 ), 54 ), 55 ), 56 ), 57 ), 58 ’elements’ => array( 59 array( 60 ’spec’ => array( 61 ’type’ => ’ZendFormElementCaptcha’, 62 ’name’ => ’captcha’, 63 ’options’ => array( 64 ’label’ => ’Please verify you are human. ’, 65 ’captcha’ => array( 66 ’class’ => ’Dumb’, 67 ), 68 ), 69 ), 70 ), 71 array( 72 ’spec’ => array( 73 ’type’ => ’ZendFormElementCsrf’, 74 ’name’ => ’security’, 75 ), 76 ), 77 array( 78 ’spec’ => array( 79 ’name’ => ’send’, 80 ’type’ => ’Submit’, 81 ’attributes’ => array( 82 ’value’ => ’Submit’, 83 ), 84 ), 85 ), 86 ), 87 // Configuration to pass on to 88 // ZendInputFilterFactory::createInputFilter() 89 ’input_filter’ => array( 90 /* ... */ 91 ), 92 )); Note that the chief difference is nesting; otherwise, the information is basically the same. 129.2. Creation via Factory 563
  • 604. Zend Framework 2 Documentation, Release 2.3.1dev The chief benefits to using the Factory are allowing you to store definitions in configuration, and usage of significant whitespace. 129.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 use ZendCaptchaAdapterInterface as CaptchaAdapter; 4 use ZendFormElement; 5 use ZendFormForm; 6 7 class ContactForm extends Form 8 { 9 protected $captcha; 10 11 public function __construct(CaptchaAdapter $captcha) 12 { 13 14 parent::__construct(); 15 16 $this->captcha = $captcha; 17 18 // add() can take either an Element/Fieldset instance, 19 // or a specification, from which the appropriate object 20 // will be built. 21 22 $this->add(array( 23 ’name’ => ’name’, 24 ’options’ => array( 25 ’label’ => ’Your name’, 26 ), 27 ’type’ => ’Text’, 28 )); 29 $this->add(array( 30 ’type’ => ’ZendFormElementEmail’, 31 ’name’ => ’email’, 32 ’options’ => array( 33 ’label’ => ’Your email address’, 34 ), 35 )); 36 $this->add(array( 37 ’name’ => ’subject’, 38 ’options’ => array( 39 ’label’ => ’Subject’, 40 ), 41 ’type’ => ’Text’, 42 )); 43 $this->add(array( 44 ’type’ => ’ZendFormElementTextarea’, 45 ’name’ => ’message’, 46 ’options’ => array( 47 ’label’ => ’Message’, 564 Chapter 129. Quick Start
  • 605. Zend Framework 2 Documentation, Release 2.3.1dev 48 ), 49 )); 50 $this->add(array( 51 ’type’ => ’ZendFormElementCaptcha’, 52 ’name’ => ’captcha’, 53 ’options’ => array( 54 ’label’ => ’Please verify you are human.’, 55 ’captcha’ => $this->captcha, 56 ), 57 )); 58 $this->add(new ElementCsrf(’security’)); 59 $this->add(array( 60 ’name’ => ’send’, 61 ’type’ => ’Submit’, 62 ’attributes’ => array( 63 ’value’ => ’Submit’, 64 ), 65 )); 66 67 // We could also define the input filter here, or 68 // lazy-create it in the getInputFilter() method. 69 } 70 } You’ll note that this example, the elements are added in the constructor. This is done to allow altering and/or config- uring 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. 129.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 // If the form doesn’t define an input filter by default, inject one. 4 $form->setInputFilter(new ContactContactFilter()); 5 6 // Get the data. In an MVC application, you might try: 7 $data = $request->getPost(); // for POST data 8 $data = $request->getQuery(); // for GET (or query string) data 9 10 $form->setData($data); 11 12 // Validate the form 13 if ($form->isValid()) { 14 $validatedData = $form->getData(); 15 } else { 16 $messages = $form->getMessages(); 17 } You can get the raw data if you want, by accessing the composed input filter. 1 $filter = $form->getInputFilter(); 2 129.4. Validating Forms 565
  • 606. Zend Framework 2 Documentation, Release 2.3.1dev 3 $rawValues = $filter->getRawValues(); 4 $nameRawValue = $filter->getRawValue(’name’); 129.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 use ZendFormElement; 4 use ZendInputFilterInputProviderInterface; 5 use ZendValidatorRegex as RegexValidator; 6 use ZendValidatorValidatorInterface; 7 8 class Color extends Element implements InputProviderInterface 9 { 10 /** 11 * Seed attributes 12 * 13 * @var array 14 */ 15 protected $attributes = array( 16 ’type’ => ’color’, 17 ); 18 19 /** 20 * @var ValidatorInterface 21 */ 22 protected $validator; 23 24 /** 25 * Get validator 26 * 27 * @return ValidatorInterface 28 */ 29 protected function getValidator() 30 { 31 if (null === $this->validator) { 32 $this->validator = new RegexValidator(’/^#[0-9a-fA-F]{6}$/’); 33 } 34 return $this->validator; 35 } 566 Chapter 129. Quick Start
  • 607. Zend Framework 2 Documentation, Release 2.3.1dev 36 37 /** 38 * Provide default input rules for this element 39 * 40 * Attaches an email validator. 41 * 42 * @return array 43 */ 44 public function getInputSpecification() 45 { 46 return array( 47 ’name’ => $this->getName(), 48 ’required’ => true, 49 ’filters’ => array( 50 array(’name’ => ’ZendFilterStringTrim’), 51 array(’name’ => ’ZendFilterStringToLower’), 52 ), 53 ’validators’ => array( 54 $this->getValidator(), 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 use ZendFormFieldset; 4 use ZendInputFilterInputFilterProviderInterface; 5 use ZendValidator; 6 7 class SenderFieldset extends Fieldset implements InputFilterProviderInterface 8 { 9 public function getInputFilterSpecification() 10 { 11 return array( 12 ’name’ => array( 13 ’required’ => true, 14 ’filters’ => array( 15 array(’name’ => ’ZendFilterStringTrim’), 16 ), 17 ), 18 ’email’ => array( 19 ’required’ => true, 20 ’filters’ => array( 21 array(’name’ => ’ZendFilterStringTrim’), 22 ), 23 ’validators’ => array( 24 new ValidatorEmailAddress(), 25 ), 26 ), 27 ); 28 } 129.5. Hinting to the Input F