SlideShare a Scribd company logo
Performance Tuning with
    Zend Framework
        Alan Seiden
      August 23, 2011
                        New York City
                          ZF Meetup
Why a ZF Performance topic?
• I’ve recently helped several clients with
  performance of their ZF apps
• Performance is important to everyone
  today
What we’ll cover tonight
• Question: Does ZF performance differ
  from regular PHP performance?
• Using ZF performance tools
  – Zend_Db_Profiler
  – Zend_Cache
• Other ZF performance optimizations
• Client side measurement and
  optimizations
ZF vs. regular PHP
• ZF is PHP
   – Framework is PHP
   – Your app is PHP
• But it’s more PHP code than your app would use if built
  from scratch
   – Meant to cover common use cases
• With ZF’s MVC, each request goes through routing,
  dispatch
• Each class contains redundant require_once() calls
   – Redundant if you use class autoloader (best performance)
   – Only in ZF 1.x. To be corrected in ZF 2.0
Zend_Db query profiler
• A good reason to use Zend_Db
• Better than manual profiling because you
  won’t miss any queries
• See the actual SQL created by Zend_Db

• One way: Firebug/FirePHP
   – In application.ini:
resources.db.params.profiler.enabled = true
resources.db.params.profiler.class   =
   "Zend_Db_Profiler_Firebug"
Query profiling viewed in FirePHP
Profiling to a log file
// a good place to put this profiling code is in the postDispatch() event of a front
    controller plugin
$db = Zend_Registry::get('db'); // defined in bootstrap

$profiler = $db->getProfiler();
$totalTime    = $profiler->getTotalElapsedSecs();
$queryCount   = $profiler->getTotalNumQueries();

foreach ($profiler->getQueryProfiles() as $i=>$query) {
    $secs = $query->getElapsedSecs();

    $msg = $i . ' - "' . $query->getQuery() . '"';
    $msg .= ', Params: ' . implode(',', $query->getQueryParams());
    $msg .= ', Time: ' . number_format($secs, 6). ' seconds';

    $messages[] = $msg;
}

$log = $queryCount . ' queries in ' . number_format($totalTime, 6)
                   . ' seconds' . "n";

$log .= "Queries:n";
$log .= implode("n", $messages);

$logger = Zend_Registry::get(‘logger’); // defined in bootstrap
$logger->debug($log);
Log file results
2011-08-18T11:34:06-04:00 DEBUG (7): 2 queries in 0.937705 seconds

Queries:
0 - "SELECT COUNT(1) AS "zend_paginator_row_count" FROM "SQHMSTP"
 LEFT JOIN "XUPMSTP" AS "UP1" ON QHAFSR = UP1.UPUID
 LEFT JOIN "XUPMSTP" AS "UP2" ON QHAUSR = UP2.UPUID
 INNER JOIN "XTVMSTP" AS "TV1" ON TV1.TVFLD = 'QHSTAT' and TV1.TVCODE = QHSTAT
 INNER JOIN "XTVMSTP" AS "TV2" ON TV2.TVFLD = 'RPTTYP' and TV2.TVCODE = QHTYPE WHERE
    (QHCOCD = '01')",
 Params: , Time: 0.820897 seconds

1 - "SELECT "SQHMSTP"."QHCASE", "SQHMSTP"."QHCHAS", (QHADMM * 10000 + QHADDD * 100 +
    QHADYY) AS "QHADDT", "SQHMSTP"."QHTYPE", "SQHMSTP"."QHDLR", "SQHMSTP"."QHSTAT",
    "SQHMSTP"."QHRPRF", "SQHMSTP"."QHCREF", "SQHMSTP"."QHSTAT", CASE WHEN (QHSTAT =
    '20' OR (QHSTAT = '40' AND QHRPRF = '')) THEN 1 ELSE 0 END AS "EDITABLE", CASE WHEN
    (QHSTAT = '20' OR QHSTAT = '40') THEN 1 ELSE 0 END AS "DELETABLE", "UP1"."UPNAME"
    AS "QHASSNAME", "UP2"."UPNAME" AS "QHAUSRNAME", "TV1"."TVDESC" AS "QHSTATDESC",
    "TV2"."TVDESC" AS "QHTYPEDESC" FROM "SQHMSTP"
 LEFT JOIN "XUPMSTP" AS "UP1" ON QHAFSR = UP1.UPUID
 LEFT JOIN "XUPMSTP" AS "UP2" ON QHAUSR = UP2.UPUID
 INNER JOIN "XTVMSTP" AS "TV1" ON TV1.TVFLD = 'QHSTAT' and TV1.TVCODE = QHSTAT
 INNER JOIN "XTVMSTP" AS "TV2" ON TV2.TVFLD = 'RPTTYP' and TV2.TVCODE = QHTYPE WHERE
    (QHCOCD = '01') ORDER BY "QHCASE" DESC FETCH FIRST 40 ROWS ONLY",
Params: , Time: 0.116808 seconds
Zend_Cache
• Flexible caching component
• Caches any kind of data: output from PHP
  scripts, complete web pages, ACL objects, query
  results

• Zend_Cache API stores cached data in your
  choice of “backends” (next slide)
Zend_Cache
• Back-ends where cached data can be stored

  –   Zend Server memory or disk cache
  –   Disk (your choice of location)
  –   Memcached
  –   APC
  –   SQLite
  –   Xcache
  –   Static (for generating static files for Apache to serve)
  –   Two-tier fast/slow
Zend_Cache configuration
• Easiest way is in application.ini
    – If you set up your app using Zend_Tool
; front-end
resources.cachemanager.database.frontend.name = Core

; lifetime of 3600 means one hour
resources.cachemanager.database.frontend.options.lifetime = 3600

; automatic_serialization enables non-strings (objects) to be cached
resources.cachemanager.database.frontend.options.automatic_serialization = true

; back-end
; ZendServer_ShMem is Zend Server’s shared memory cache
resources.cachemanager.database.backend.name = "ZendServer_ShMem"
resources.cachemanager.database.backend.customBackendNaming = true
Caching tip for Zend_Db_Table
• Do cache metadata (table/field definitions) if you
  use Zend_Db_Table
• Otherwise you will have a performance hit
• The degree of performance penalty of always
  reading metadata depends on the database
  server
• Play it safe and cache this metadata
   – Assuming tables/fields are relatively constant

// in application.ini
// (“database” cache was defined on previous slide)
resources.db.defaultMetadataCache = "database"
Use an opcode/bytecode cache
• Frameworks add classes and code to an app
• PHP ordinarily must read/interpret/compile all
  that code on each request
• A bytecode cache stores the “compiled”
  bytecode in memory after first execution,
  speeding subsequent runs
• Examples of bytecode caches:
  –   Zend Server’s Optimizer+
  –   APC
  –   XCache
  –   Windows Cache Extension for PHP
ZF Performance Guide
http://framework.zend.com/manual/en/performance.html

• Covers several topics related to ZF performance
• Written by the ZF development team

• Among its recommendations:
   – Avoid “action view helper”: invokes dispatch cycle
      • Replace with view helpers that query a model directly
   – “Use partial() only when really necessary”
      • Partial() clones the whole View object. Use render() if do not
        need a new, clean View object
   – And…
Class loading
• The issues around class loading are given
  special attention in the Performance Guide

• In particular, the “autoloader/require_once()”
  issue is the most frequently discussed
  performance “flaw” of ZF 1.x

• It will be fixed in ZF 2.0

• Details of 1.x “flaw” on next slide......
Autoloader/require_once() issue
• The good:
   – ZF’s autoloader is deemed a well performing component
       • Enabled in /public/index.php like so:
           require_once 'Zend/Loader/Autoloader.php';
           Zend_Loader_Autoloader::getInstance();



• The bad:
   – Even though autoloader loads classes as needed, each class
     executes require_once() statements at the top for each class it
     might need

• Solution: remove require_once() statements from almost
  every ZF class
   – P.S. Matthew Weier O’Phinney says, “this will only improve
     speed if an opcode cache is used.”
How to remove require_once()
Official UNIX way
% cd path/to/ZendFramework/library
% find . -name '*.php' -not -wholename
   '*/Loader/Autoloader.php'  -not -wholename
   '*/Application.php' -print0 |  xargs -0 sed --regexp-
   extended --in-place 's/(require_once)/// 1/g'


Doesn’t remove it from Autoloader.php and
 Application.php because it’s needed there!
Removing require_once() #2
Using PHP
// from http://pastebin.com/wHKJZ68e

chdir (‘mylocationlibraryZend');

foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator('.')) as $o_File)
    {
    if (
          '.php' === substr($o_File, -4) &&
          false === strrpos($o_File, '.' . DIRECTORY_SEPARATOR . 'Loader' .
    DIRECTORY_SEPARATOR . 'Autoloader.php') &&
          false === strrpos($o_File, '.' . DIRECTORY_SEPARATOR . 'Application.php')) {
          $s_Code = preg_replace('/^(s*)(require_once)/im', '1// 2',
    file_get_contents($o_File), -1, $i_Replacements);
          if ($i_Replacements > 0) {
                    echo $o_File, ' with ', $i_Replacements, ' replacements.', PHP_EOL;
                    file_put_contents($o_File, $s_Code);
          }
    }
}
Keep an eye on the front end
• Otherwise known as the “client” side
• Includes .js, .css, images, and AJAX calls
• Check it out with Firebug’s “Net” panel or your
  favorite tool

• Example coming up...
HTTP requests




In particular, beware if several AJAX calls must execute on page load
(not shown here) in order for page to render
Apache rewrite rule
.htaccess usually looks like this:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME}
RewriteRule ^.*$ – [NC,L]
RewriteRule ^.*$ index.php [NC,L]


• Any request that’s not a real file gets routed into
  ZF/PHP
• What’s the performance flaw?
Nonexistent files
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME}
RewriteRule ^.*$ – [NC,L]
RewriteRule ^.*$ index.php [NC,L]


• Nonexistent files (whether favicon.ico or
  my.hacker.getya) get routed to ZF, putting load
  on app server, before generating a 404 not
  found error
• Shouldn’t the web server handle 404?
Solution
• I haven’t found a perfect solution

• To intercept normal “file not found” errors in Apache:
   –   RewriteRule !.(js|ico|gif|jpg|png|css|html|txt|log)$ index.php


• If I’m confident that app URLs shouldn’t have
  any periods/dots in ZF URLs:
   –   RewriteRule !.([^.]+)$ index.php
   – ZF will only receive period-free URLs
   – Apache can then catch “weird” URLs such as
     “w00tw00t.at.ISC.SAN” (I found this in a customer’s Apache log)

• Demonstration on next slide

• Better idea? Send to alan@alanseiden.com
404 Before/after new rule




Before             After
Further learning
• Attend the NYC Web Performance Meetup
• Follow me at @alanseiden
• Keep coming to our ZF meetup:
  http://www.meetup.com/ZendFramework-NYCmetro/

• Attend ZendCon, Oct. 17-20, 2011

• Share your discoveries—you are welcome to present at
  the ZF Meetup
New York City area Zend Framework Meetup
                 http://www.meetup.com/ZendFramework-NYCmetro/
                 Affiliated with http://www.nyphp.org/



Thanks for attending Performance Tuning with Zend Framework
presented on Aug. 23, 2011 by


Alan Seiden      http://www.alanseiden.com
                 alan@alanseiden.com
                 Twitter: @alanseiden




Sign up to hear about all our ZF meetups at
http://www.meetup.com/ZendFramework-NYCmetro/

More Related Content

PDF
DB2 and PHP in Depth on IBM i
PDF
Strategic Modernization with PHP on IBM i
PDF
PHP Toolkit from Zend and IBM: Open Source on IBM i
PDF
Create a welcoming development environment on IBM i
PDF
From Zero to ZF: Your first zend framework project on ibm i
PPTX
PHP on IBM i Tutorial
PDF
PHP Batch Jobs on IBM i
PPTX
Getting started with PHP on IBM i
DB2 and PHP in Depth on IBM i
Strategic Modernization with PHP on IBM i
PHP Toolkit from Zend and IBM: Open Source on IBM i
Create a welcoming development environment on IBM i
From Zero to ZF: Your first zend framework project on ibm i
PHP on IBM i Tutorial
PHP Batch Jobs on IBM i
Getting started with PHP on IBM i

What's hot (20)

KEY
Zend_Tool: Practical use and Extending
PDF
Zend Core on IBM i - Security Considerations
PDF
Web services on IBM i with PHP and Zend Framework
PDF
Zend_Cache: how to improve the performance of PHP applications
PDF
IBM i: Fertile Ground for PHP Developers
PPTX
Fundamentals of performance tuning PHP on IBM i
PPTX
Zend Products and PHP for IBMi
PDF
Browser tools that make web development easier
PPTX
Install MariaDB on IBM i - Tips, troubleshooting, and more
PPT
PHP on Windows - What's New
PPTX
PHP Installed on IBM i - the Nickel Tour
PDF
Running open source PHP applications on you IBM i
PDF
Php Dependency Management with Composer ZendCon 2016
PPTX
PHP and Platform Independance in the Cloud
PPTX
Zend con 2016 bdd with behat for beginners
PPTX
Sizing your alfresco platform
PDF
Dutch php conference_2010_opm
PPT
Edp bootstrapping a-software_company
PDF
Developing High Performance and Scalable ColdFusion Application Using Terraco...
PDF
Cfml features modern_coding
Zend_Tool: Practical use and Extending
Zend Core on IBM i - Security Considerations
Web services on IBM i with PHP and Zend Framework
Zend_Cache: how to improve the performance of PHP applications
IBM i: Fertile Ground for PHP Developers
Fundamentals of performance tuning PHP on IBM i
Zend Products and PHP for IBMi
Browser tools that make web development easier
Install MariaDB on IBM i - Tips, troubleshooting, and more
PHP on Windows - What's New
PHP Installed on IBM i - the Nickel Tour
Running open source PHP applications on you IBM i
Php Dependency Management with Composer ZendCon 2016
PHP and Platform Independance in the Cloud
Zend con 2016 bdd with behat for beginners
Sizing your alfresco platform
Dutch php conference_2010_opm
Edp bootstrapping a-software_company
Developing High Performance and Scalable ColdFusion Application Using Terraco...
Cfml features modern_coding
Ad

Similar to Performance tuning with zend framework (20)

PDF
Caching with Memcached and APC
PPT
nodejs_at_a_glance, understanding java script
PPT
nodejs_at_a_glance.ppt
KEY
Site Performance - From Pinto to Ferrari
PDF
The Solar Framework for PHP 5 (2010 Confoo)
PDF
php & performance
PDF
Top ten-list
PDF
PHP & Performance
PPT
Heavy Web Optimization: Backend
KEY
Zend framework: Getting to grips (ZF1)
PDF
Scaling PHP apps
PDF
Tips
KEY
Webinar: Zend framework Getting to grips (ZF1)
PDF
6 tips for improving ruby performance
PDF
Zend Server Data Caching
KEY
Scaling php applications with redis
PDF
Building Testable PHP Applications
PPTX
Z-Ray: A customizable development tool belt (Zendcon 2016)
PDF
Grâce aux tags Varnish, j'ai switché ma prod sur Raspberry Pi
ODP
CodeIgniter PHP MVC Framework
Caching with Memcached and APC
nodejs_at_a_glance, understanding java script
nodejs_at_a_glance.ppt
Site Performance - From Pinto to Ferrari
The Solar Framework for PHP 5 (2010 Confoo)
php & performance
Top ten-list
PHP & Performance
Heavy Web Optimization: Backend
Zend framework: Getting to grips (ZF1)
Scaling PHP apps
Tips
Webinar: Zend framework Getting to grips (ZF1)
6 tips for improving ruby performance
Zend Server Data Caching
Scaling php applications with redis
Building Testable PHP Applications
Z-Ray: A customizable development tool belt (Zendcon 2016)
Grâce aux tags Varnish, j'ai switché ma prod sur Raspberry Pi
CodeIgniter PHP MVC Framework
Ad

Recently uploaded (20)

PDF
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PDF
NewMind AI Weekly Chronicles - August'25-Week II
PDF
Empathic Computing: Creating Shared Understanding
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PDF
Assigned Numbers - 2025 - Bluetooth® Document
PDF
MIND Revenue Release Quarter 2 2025 Press Release
PPTX
Programs and apps: productivity, graphics, security and other tools
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
Encapsulation theory and applications.pdf
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PPTX
Machine Learning_overview_presentation.pptx
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PPTX
Spectroscopy.pptx food analysis technology
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton
Agricultural_Statistics_at_a_Glance_2022_0.pdf
NewMind AI Weekly Chronicles - August'25-Week II
Empathic Computing: Creating Shared Understanding
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Assigned Numbers - 2025 - Bluetooth® Document
MIND Revenue Release Quarter 2 2025 Press Release
Programs and apps: productivity, graphics, security and other tools
Digital-Transformation-Roadmap-for-Companies.pptx
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Encapsulation theory and applications.pdf
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
Chapter 3 Spatial Domain Image Processing.pdf
Mobile App Security Testing_ A Comprehensive Guide.pdf
The Rise and Fall of 3GPP – Time for a Sabbatical?
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Machine Learning_overview_presentation.pptx
Dropbox Q2 2025 Financial Results & Investor Presentation
Building Integrated photovoltaic BIPV_UPV.pdf
Spectroscopy.pptx food analysis technology

Performance tuning with zend framework

  • 1. Performance Tuning with Zend Framework Alan Seiden August 23, 2011 New York City ZF Meetup
  • 2. Why a ZF Performance topic? • I’ve recently helped several clients with performance of their ZF apps • Performance is important to everyone today
  • 3. What we’ll cover tonight • Question: Does ZF performance differ from regular PHP performance? • Using ZF performance tools – Zend_Db_Profiler – Zend_Cache • Other ZF performance optimizations • Client side measurement and optimizations
  • 4. ZF vs. regular PHP • ZF is PHP – Framework is PHP – Your app is PHP • But it’s more PHP code than your app would use if built from scratch – Meant to cover common use cases • With ZF’s MVC, each request goes through routing, dispatch • Each class contains redundant require_once() calls – Redundant if you use class autoloader (best performance) – Only in ZF 1.x. To be corrected in ZF 2.0
  • 5. Zend_Db query profiler • A good reason to use Zend_Db • Better than manual profiling because you won’t miss any queries • See the actual SQL created by Zend_Db • One way: Firebug/FirePHP – In application.ini: resources.db.params.profiler.enabled = true resources.db.params.profiler.class = "Zend_Db_Profiler_Firebug"
  • 7. Profiling to a log file // a good place to put this profiling code is in the postDispatch() event of a front controller plugin $db = Zend_Registry::get('db'); // defined in bootstrap $profiler = $db->getProfiler(); $totalTime = $profiler->getTotalElapsedSecs(); $queryCount = $profiler->getTotalNumQueries(); foreach ($profiler->getQueryProfiles() as $i=>$query) { $secs = $query->getElapsedSecs(); $msg = $i . ' - "' . $query->getQuery() . '"'; $msg .= ', Params: ' . implode(',', $query->getQueryParams()); $msg .= ', Time: ' . number_format($secs, 6). ' seconds'; $messages[] = $msg; } $log = $queryCount . ' queries in ' . number_format($totalTime, 6) . ' seconds' . "n"; $log .= "Queries:n"; $log .= implode("n", $messages); $logger = Zend_Registry::get(‘logger’); // defined in bootstrap $logger->debug($log);
  • 8. Log file results 2011-08-18T11:34:06-04:00 DEBUG (7): 2 queries in 0.937705 seconds Queries: 0 - "SELECT COUNT(1) AS "zend_paginator_row_count" FROM "SQHMSTP" LEFT JOIN "XUPMSTP" AS "UP1" ON QHAFSR = UP1.UPUID LEFT JOIN "XUPMSTP" AS "UP2" ON QHAUSR = UP2.UPUID INNER JOIN "XTVMSTP" AS "TV1" ON TV1.TVFLD = 'QHSTAT' and TV1.TVCODE = QHSTAT INNER JOIN "XTVMSTP" AS "TV2" ON TV2.TVFLD = 'RPTTYP' and TV2.TVCODE = QHTYPE WHERE (QHCOCD = '01')", Params: , Time: 0.820897 seconds 1 - "SELECT "SQHMSTP"."QHCASE", "SQHMSTP"."QHCHAS", (QHADMM * 10000 + QHADDD * 100 + QHADYY) AS "QHADDT", "SQHMSTP"."QHTYPE", "SQHMSTP"."QHDLR", "SQHMSTP"."QHSTAT", "SQHMSTP"."QHRPRF", "SQHMSTP"."QHCREF", "SQHMSTP"."QHSTAT", CASE WHEN (QHSTAT = '20' OR (QHSTAT = '40' AND QHRPRF = '')) THEN 1 ELSE 0 END AS "EDITABLE", CASE WHEN (QHSTAT = '20' OR QHSTAT = '40') THEN 1 ELSE 0 END AS "DELETABLE", "UP1"."UPNAME" AS "QHASSNAME", "UP2"."UPNAME" AS "QHAUSRNAME", "TV1"."TVDESC" AS "QHSTATDESC", "TV2"."TVDESC" AS "QHTYPEDESC" FROM "SQHMSTP" LEFT JOIN "XUPMSTP" AS "UP1" ON QHAFSR = UP1.UPUID LEFT JOIN "XUPMSTP" AS "UP2" ON QHAUSR = UP2.UPUID INNER JOIN "XTVMSTP" AS "TV1" ON TV1.TVFLD = 'QHSTAT' and TV1.TVCODE = QHSTAT INNER JOIN "XTVMSTP" AS "TV2" ON TV2.TVFLD = 'RPTTYP' and TV2.TVCODE = QHTYPE WHERE (QHCOCD = '01') ORDER BY "QHCASE" DESC FETCH FIRST 40 ROWS ONLY", Params: , Time: 0.116808 seconds
  • 9. Zend_Cache • Flexible caching component • Caches any kind of data: output from PHP scripts, complete web pages, ACL objects, query results • Zend_Cache API stores cached data in your choice of “backends” (next slide)
  • 10. Zend_Cache • Back-ends where cached data can be stored – Zend Server memory or disk cache – Disk (your choice of location) – Memcached – APC – SQLite – Xcache – Static (for generating static files for Apache to serve) – Two-tier fast/slow
  • 11. Zend_Cache configuration • Easiest way is in application.ini – If you set up your app using Zend_Tool ; front-end resources.cachemanager.database.frontend.name = Core ; lifetime of 3600 means one hour resources.cachemanager.database.frontend.options.lifetime = 3600 ; automatic_serialization enables non-strings (objects) to be cached resources.cachemanager.database.frontend.options.automatic_serialization = true ; back-end ; ZendServer_ShMem is Zend Server’s shared memory cache resources.cachemanager.database.backend.name = "ZendServer_ShMem" resources.cachemanager.database.backend.customBackendNaming = true
  • 12. Caching tip for Zend_Db_Table • Do cache metadata (table/field definitions) if you use Zend_Db_Table • Otherwise you will have a performance hit • The degree of performance penalty of always reading metadata depends on the database server • Play it safe and cache this metadata – Assuming tables/fields are relatively constant // in application.ini // (“database” cache was defined on previous slide) resources.db.defaultMetadataCache = "database"
  • 13. Use an opcode/bytecode cache • Frameworks add classes and code to an app • PHP ordinarily must read/interpret/compile all that code on each request • A bytecode cache stores the “compiled” bytecode in memory after first execution, speeding subsequent runs • Examples of bytecode caches: – Zend Server’s Optimizer+ – APC – XCache – Windows Cache Extension for PHP
  • 14. ZF Performance Guide http://framework.zend.com/manual/en/performance.html • Covers several topics related to ZF performance • Written by the ZF development team • Among its recommendations: – Avoid “action view helper”: invokes dispatch cycle • Replace with view helpers that query a model directly – “Use partial() only when really necessary” • Partial() clones the whole View object. Use render() if do not need a new, clean View object – And…
  • 15. Class loading • The issues around class loading are given special attention in the Performance Guide • In particular, the “autoloader/require_once()” issue is the most frequently discussed performance “flaw” of ZF 1.x • It will be fixed in ZF 2.0 • Details of 1.x “flaw” on next slide......
  • 16. Autoloader/require_once() issue • The good: – ZF’s autoloader is deemed a well performing component • Enabled in /public/index.php like so: require_once 'Zend/Loader/Autoloader.php'; Zend_Loader_Autoloader::getInstance(); • The bad: – Even though autoloader loads classes as needed, each class executes require_once() statements at the top for each class it might need • Solution: remove require_once() statements from almost every ZF class – P.S. Matthew Weier O’Phinney says, “this will only improve speed if an opcode cache is used.”
  • 17. How to remove require_once() Official UNIX way % cd path/to/ZendFramework/library % find . -name '*.php' -not -wholename '*/Loader/Autoloader.php' -not -wholename '*/Application.php' -print0 | xargs -0 sed --regexp- extended --in-place 's/(require_once)/// 1/g' Doesn’t remove it from Autoloader.php and Application.php because it’s needed there!
  • 18. Removing require_once() #2 Using PHP // from http://pastebin.com/wHKJZ68e chdir (‘mylocationlibraryZend'); foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator('.')) as $o_File) { if ( '.php' === substr($o_File, -4) && false === strrpos($o_File, '.' . DIRECTORY_SEPARATOR . 'Loader' . DIRECTORY_SEPARATOR . 'Autoloader.php') && false === strrpos($o_File, '.' . DIRECTORY_SEPARATOR . 'Application.php')) { $s_Code = preg_replace('/^(s*)(require_once)/im', '1// 2', file_get_contents($o_File), -1, $i_Replacements); if ($i_Replacements > 0) { echo $o_File, ' with ', $i_Replacements, ' replacements.', PHP_EOL; file_put_contents($o_File, $s_Code); } } }
  • 19. Keep an eye on the front end • Otherwise known as the “client” side • Includes .js, .css, images, and AJAX calls • Check it out with Firebug’s “Net” panel or your favorite tool • Example coming up...
  • 20. HTTP requests In particular, beware if several AJAX calls must execute on page load (not shown here) in order for page to render
  • 21. Apache rewrite rule .htaccess usually looks like this: RewriteEngine On RewriteCond %{REQUEST_FILENAME} -s [OR] RewriteCond %{REQUEST_FILENAME} -l [OR] RewriteCond %{REQUEST_FILENAME} RewriteRule ^.*$ – [NC,L] RewriteRule ^.*$ index.php [NC,L] • Any request that’s not a real file gets routed into ZF/PHP • What’s the performance flaw?
  • 22. Nonexistent files RewriteEngine On RewriteCond %{REQUEST_FILENAME} -s [OR] RewriteCond %{REQUEST_FILENAME} -l [OR] RewriteCond %{REQUEST_FILENAME} RewriteRule ^.*$ – [NC,L] RewriteRule ^.*$ index.php [NC,L] • Nonexistent files (whether favicon.ico or my.hacker.getya) get routed to ZF, putting load on app server, before generating a 404 not found error • Shouldn’t the web server handle 404?
  • 23. Solution • I haven’t found a perfect solution • To intercept normal “file not found” errors in Apache: – RewriteRule !.(js|ico|gif|jpg|png|css|html|txt|log)$ index.php • If I’m confident that app URLs shouldn’t have any periods/dots in ZF URLs: – RewriteRule !.([^.]+)$ index.php – ZF will only receive period-free URLs – Apache can then catch “weird” URLs such as “w00tw00t.at.ISC.SAN” (I found this in a customer’s Apache log) • Demonstration on next slide • Better idea? Send to [email protected]
  • 24. 404 Before/after new rule Before After
  • 25. Further learning • Attend the NYC Web Performance Meetup • Follow me at @alanseiden • Keep coming to our ZF meetup: http://www.meetup.com/ZendFramework-NYCmetro/ • Attend ZendCon, Oct. 17-20, 2011 • Share your discoveries—you are welcome to present at the ZF Meetup
  • 26. New York City area Zend Framework Meetup http://www.meetup.com/ZendFramework-NYCmetro/ Affiliated with http://www.nyphp.org/ Thanks for attending Performance Tuning with Zend Framework presented on Aug. 23, 2011 by Alan Seiden http://www.alanseiden.com [email protected] Twitter: @alanseiden Sign up to hear about all our ZF meetups at http://www.meetup.com/ZendFramework-NYCmetro/