Podcast on Running PHP on Windows

Peter Laudati & Dmitry Lyalin host the edu-training Connected Show developer podcast on cloud computing and interoperability. I met Peter in Chicago during Tek-X and we along with Don and Wade recorded a podcast on evolution of PHP on the Windows Platform. We talked about the improvements made to Windows in order to support PHP, including: Fast CGI, WinCache, PHP 5.3, the Web PI, and the SQL Server driver for PHP. Check out episode #31, “PHP On Windows” on Connected Show. Interview starts at 24:31.


If you like what you hear, check out previous episodes of the Connected Show at www.connectedshow.com.  You can subscribe on iTunes or Zune.  Peter and Dmitry publish new episodes approximately every two weeks!


Goodness of dynamic maxInstances in FastCGI

One of the major pain points in FastCGI 1.0 was that users were required to set maxInstances to a number which works best for their application. Many users didn’t tune this value and were running FastCGI with default value of maxInstances which didn’t give them optimal performance. We recommended users to set maxInstances to (10 * number of processors) if they don’t want to go through the pain of finding the ideal maxInstances for them which worked much better than default value but still didn’t give optimal performance. Even when users fine-tuned maxInstances to ideal value, they didn’t always get best possible performance at all times due to variations in traffic, server/application configuration changes and code changes.
In IIS 7.5 we fixed this pain point by making FastCGI module capable of monitoring system state and adjust this number dynamically to give best possible performance. This feature is referred to as dynamic maxInstances or auto maxInstances and can be enabled by setting maxInstances value to 0. As we intend to maintain a consistent platform for PHP developers on WinXP and above, we have added this feature to FastCGI ISAPI 1.5 and made it available on IIS 7.0 as well (read more here). Last week I spent some time comparing dynamic maxInstances results with suggested maxInstances value (10 * processor count) results and below is what I got.
Hardware: Quad core machine with 4 GB RAM
Application: Joomla


MaxInstances = 40
MaxInstances = 0
%age difference
Path Length (less is good)
Response Time (less is good)
844 ms
783 ms
Context switches/sec (less is good)
Requests/Sec (more is good)
As you can see, all performance parameters improved considerably just by changing maxInstances to 0. Our testing showed similar improvements in performance of other PHP applications as well. So we changed the default value of maxInstances to 0 in FastCGI ISAPI 1.5 and Win7. Note that if a value is explicitly set in fcgiext.ini or applicationHost.config, that value will override default value. Due to restrictions of QFE release process, we couldn’t change the default of maxInstances in IIS 7.0. So you are required to enable dynamic maxInstances after install this update on IIS 7.0.
We typically run many applications on our performance bench machines and were required to choose one maxInstances number for all applications. Choosing a different maxInstances number for each application was a better way to do performance testing but that was painful. Now with dynamic maxInstances, we get optimal performance for each application without any pain. Our performance testing is loving this feature and I hope you love it too.

How to add traces to IIS log from PHP

One of the new features added to FastCGI module in IIS 7.5 is ability to use IIS tracing infrastructure from FastCGI applications. This feature request was made by a customer who wanted to consolidate logging of the request processing in one log. All IIS events were present in IIS trace logs but traces generated by PHP applications were separate. So IIS trace log didn’t give a holistic view for PHP applications. ASP.Net already has a way to use IIS traces. So this limitation was specific to PHP. We closed this gap in PHP by providing a way for PHP developers to send traces to IIS using FastCGI’s STDERR stream which are then extracted by FastCGI module and sent to IIS tracing subsystem. This feature has been included in KB 980363 so that this can be made to work on IIS 7.0 as well. On IIS6 and below, these trace messages will be ignored as there is no tracing subsystem available.
FastCGI module parses STDERR stream looking for markers which identify the content as trace messages. Messages starting with “IIS_TRACE_ERROR:” are parsed as errors, “IIS_TRACE_WARNING:” as warnings and “IIS_TRACE_INFO” as info messages. Message end is recognized by marker “:IIS_TRACE_END”. Any function in PHP which can write to STDERR stream can be used to send these traces to FastCGI. So calling error_log(“IIS_TRACE_ERROR:This is an error.:IIS_TRACE_END”) will appear in IIS trace log as an error. Traces appear for CGI trace provider on IIS 7.0 and FastCGI trace provider on IIS 7.5. Trace messages are removed from STDERR stream before STDERR stream content goes through StderrMode logic so that traces don’t disturb existing STDERR behavior. Below is a simple helper class which you can use to generate trace messages.
class IIS_TRACE {
 const NONE    = 0;
 const ERROR   = 1;
 const WARNING = 2;
 const INFO    = 3;
 private static $verbosity = self::NONE;
 private static $traceErrorStart   = "IIS_TRACE_ERROR:";
 private static $traceWarningStart = "IIS_TRACE_WARNING:";
 private static $traceInfoStart    = "IIS_TRACE_INFO:";
 private static $traceEnd          = ":IIS_TRACE_END";
 public static function SetVerbosity ( $verbose )
    if( $verbose != self::NONE    && $verbose != self::ERROR   &&
        $verbose != self::WARNING && $verbose != self::INFO )
        throw new Exception( 'Function SetVerbosity called with invalid value' );
    self::$verbosity = $verbose;
 public static function WriteError ( $msg )
    if (self::$verbosity >= self::ERROR )
        error_log( self::$traceErrorStart . $msg . self::$traceEnd );
 public static function WriteWarning ( $msg )
    if (self::$verbosity >= self::WARNING )
        error_log( self::$traceWarningStart . $msg . self::$traceEnd );
 public static function WriteInfo ( $msg )
    if (self::$verbosity >= self::INFO )
    error_log( self::$traceInfoStart . $msg .self::$traceEnd );
With this class present in your code, you can use WriteInfo, WriteWarning, WriteError functions available in IIS_TRACE class in your code to add traces to IIS trace log as below. When you have tracing enabled, these traces will appear with all other information about request processing.
If($percent > 100)
    IIS_TRACE::WriteError(‘Invalid data provided. Percent = ’ . $percent);
Hope this helps.

Application specific mapping in FastCGI ISAPI 1.5

IIS team recently released FastCGI ISAPI 1.5 for IIS 5.1 and IIS 6.0. One of the major new features added in this release is per application process pools. Per application process pools allow users to specify a unique FastCGI mapping for a particular web application in fcgiext.ini. Using this functionality administrators can now choose to run a web application with PHP version which can be different from what is used for rest of the PHP applications in the same site or the server. This was always possible in IIS 7.0 and 7.5 by overriding PHP handler in web.config but not on WinXP and Win2K3. Version 1.0 of FastCGI ISAPI only allowed specifying a different process pool at a site level granularity. So if a site has both wordpress and gallery installed, it wasn’t possible to use different PHP versions for running them. With FastCGI 1.5 that is possible now. For providing application specific mappings, we extended site specific mapping feature. Let’s learn application specific mapping by comparing it with site specific mapping.


Site specific mapping
Application specific mapping


Above configuration will make FastCGI module pick PHP_53 mapping for all sites but use PHP_52 mapping for site with id 1234.
FastCGI module will pick PHP_520 mapping for gallery and PHP_530 for wordpress defined under site 1234. All other php applications under site 1234 will be handled using PHP_526 mapping. Remaining sites will use PHP_530 mapping.

Application path’s format for fcgiext.ini is “/LM/W3SVC/<siteId>/ROOT/<appname>”. In this format “/LM” and “/W3SVC” parts are option. “/Root” can be emitted for applications pointing to root of the site.

fcgiconfig.js -add -section:php_530 -extension:php -path:c:\php530\php-cgi.exe -site:1234
fcgiconfig.js -add -section:php_530 -extension:php -path:c:\php530\php-cgi.exe -application:/w3svc/1234/root/wordpress
Picking the right mapping
Among all the formats (including wildcard formats) which are accepted in fcgiext.ini mappings, FastCGI goes through following logic to pick which mapping to use for each request.
·         If mapping for current  application is present, it takes highest priority and is used. E.g. php:/w3svc/1234/root/drupal=ApplicationHandler
·         If wildcard mapping for the current application is present, it is used next. E.g. *:/w3svc/1234/root/drupal=ApplicationHandler
·         If mapping for a particular site is present but application specific mapping is not present, than that is used. E.g. php:1234=SiteHandler
·         If site specific and application specific mapping is not present, FastCGI module look for wildcard mapping for a particular site. E.g. *:1234=SiteHandler
·         If none of the above is present, mapping for the file extension takes priority. E.g. php=ExtensionHandler
·         Wildcard mapping comes last. *=WildcardHandler
If FastCGI doesn’t find a mapping after going through the logic above, an error is returned.
What’s coming next
Now that applications can be configured to run using a particular PHP version on IIS 5.1 and above, our next goal is to support multiple PHP versions side-by-side in WebPI. This would require PHP installer to support side-by-side install which is also something we are looking at. Once PHP installer has the required support, we will list different PHP versions in WebPI and let users install multiple versions of PHP. Additionally users will be able to target a particular PHP version when installing a PHP application using WebPI.
Hope this helps.

Migrating FastCGI configuration from IIS 5.1/6.0 to IIS 7.0/7.5


As you know FastCGI functionality on IIS 5.1 and IIS 6.0 is provided by FastCGI ISAPI extension which is available as an independent download. On IIS 7.0 and beyond, FastCGI functionality is provided by IIS FastCGI module which comes with the operating system. These components use different configuration stores to store the settings that affect their behavior. FastCGI ISAPI uses an INI file named fcgiext.ini as its configuration store whereas FastCGI module keeps configuration in applicationHost.config in xml format with rest of IIS configuration. Before, migrating from IIS6 to IIS7 involved migrating fcgiext.ini configuration to FastCGI module manually as there was no automated way to do that. MSDeploy only had functionality to migrate metabase configuration but because FastCGI ISAPI configuration is kept separately in an INI file, that wasn’t possible.


MSDeploy v1 RTW which is released today include a new provider named fcgiextConfig to automate task of migrating FastCGI ISAPI INI configuration to FastCGI module section. FcgiextConfig provider accepts a path which can be “APPHOST” or ”INI”. When path “APPHOST” is specified, configuration from system.webServer/fastCgi section in applicationHost.config is read. Path “APPHOST” should only be used on systems running FastCGI module (i.e. IIS7 and beyond). When path “INI” is specified, this provider reads the configuration from file “%windir%\system32\inetsrv\fcgiext.ini” and produce xml which looks like FastCGI module configuration. Once INI settings are mapped to produce the xml, msdeploy engine can compare configuration of FastCGI ISAPI with FastCGI module and make changes to FastCGI module configuration as required. Again, path “INI” can only be used on a machine which is running FastCGI ISAPI.


Dump command to dump INI settings looks like following.
        msdeploy –verb:dump –source:fcgiExtConfig=ini –xml
        msdeploy –verb:dump –source:fcgiExtConfig=apphost –xml

Sync command to move INI settings to FastCGI module section is following.
        msdeploy –verb:sync –source:fcgiExtConfig=ini –dest:fcgiExtConfig=apphost -whatif


  Here is how INI settings are mapped to system.webServer/fastCgi settings. For each entry   “<extension>:<optionalsiteid>=<sectionname>” under [Types] section of fcgiext.ini, we read the settings under section [<sectionname>]. If optional site id is present, we look for an entry for the same extension but without site id. If an entry is found, we use configuration under this entry as base set of settings for the site specific entry. So if “php=basephp” and “php:1=site1” entries are present in INI, settings under section [basephp] provide base values and then settings under section [site1] override base settings to generate configuration for “php:1” process pool. If a property is not present in the INI file, we assume the default value as assumed by FastCGI ISAPI extension. A FastCGI process pool entry is created for each INI entry with a valid ExePath. Various INI file settings are mapped to a FastCGI process pool as below.

fcgiext.ini configuration setting system.webServer/fastCgi/application property
ExePath fullPath
Arguments arguments
QueueLength queueLength
MaxInstances maxInstances
IdleTimeout idleTimeout
ActivityTimeout activityTimeout
RequestTimeout requestTimeout
InstanceMaxRequests instanceMaxRequests
FlushNamedPipe flushNamedPipe
Protocol protocol
RapidFailsPerMinute rapidFailsPerMinute
EnvironmentVars environmentVariables
ResponseBufferLimit Ignored
IgnoreExistingFiles Ignored
IgnoreDirectories Ignored
UnhealthyOnQueueFull Ignored

If the source version is FastCGI ISAPI 1.5, some additional properties get picked and mapped as below.

StderrMode stderrMode
MonitorChangesTo MonitorChangesTo
SignalBeforeTerminateSeconds signalBeforeTerminateSeconds

If FastCGI ISAPI version on source and FastCGI module version on destination are not compatible, fcgiextConfig provider will block the migration. Note that fcgiextConfig provider doesn’t take care of migrating the relevant IIS ScriptMaps. You can migrate ScriptMaps using a separate msdeploy migrate operation.

Hope this helps.