PageRenderTime 53ms CodeModel.GetById 15ms app.highlight 30ms RepoModel.GetById 1ms app.codeStats 0ms

/tags/stable-1.0.3/Server/Config/PHPConfigHelper.cs

#
C# | 1267 lines | 1040 code | 163 blank | 64 comment | 182 complexity | 9756d764572f6e574f9570314e918e5d MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1//-----------------------------------------------------------------------
   2// <copyright>
   3// Copyright (C) Ruslan Yakushev for the PHP Manager for IIS project.
   4//
   5// This file is subject to the terms and conditions of the Microsoft Public License (MS-PL).
   6// See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL for more details.
   7// </copyright>
   8//----------------------------------------------------------------------- 
   9
  10using System;
  11using System.Collections;
  12using System.Collections.Generic;
  13using System.Diagnostics;
  14using System.Globalization;
  15using System.IO;
  16using Microsoft.Web.Administration;
  17using Microsoft.Web.Management.Server;
  18using Web.Management.PHP.DefaultDocument;
  19using Web.Management.PHP.FastCgi;
  20using Web.Management.PHP.Handlers;
  21
  22namespace Web.Management.PHP.Config
  23{   
  24
  25    /// <summary>
  26    /// Provides functions to register PHP with IIS and to manage IIS and PHP configuration.
  27    /// </summary>
  28    internal sealed class PHPConfigHelper
  29    {
  30
  31        private ManagementUnit _managementUnit;
  32
  33        private ApplicationElement _currentFastCgiApplication;
  34        private HandlerElement _currentPHPHandler;
  35        private HandlersCollection _handlersCollection;
  36        private FastCgiApplicationCollection _fastCgiApplicationCollection;
  37        private FilesCollection _defaultDocumentCollection;
  38        private string _phpIniFilePath;
  39        private string _phpDirectory;
  40        private PHPRegistrationType _registrationType;
  41
  42        public PHPConfigHelper(ManagementUnit mgmtUnit)
  43        {
  44            _managementUnit = mgmtUnit;
  45            Initialize();
  46        }
  47
  48        public string PHPDirectory
  49        {
  50            get
  51            {
  52                return _phpDirectory;
  53            }
  54        }
  55
  56        public string PHPIniFilePath
  57        {
  58            get
  59            {
  60                return _phpIniFilePath;
  61            }
  62        }
  63
  64        private void ApplyRecommendedFastCgiSettings(ArrayList configIssueIndexes)
  65        {
  66            bool iisChangeHappened = false;
  67
  68            foreach (int configIssueIndex in configIssueIndexes)
  69            {
  70                switch (configIssueIndex)
  71                {
  72                    case PHPConfigIssueIndex.DefaultDocument:
  73                        {
  74                            iisChangeHappened = ChangeDefaultDocument() || iisChangeHappened;
  75                            break;
  76                        }
  77                    case PHPConfigIssueIndex.ResourceType:
  78                        {
  79                            iisChangeHappened = ChangeResourceType() || iisChangeHappened;
  80                            break;
  81                        }
  82                    case PHPConfigIssueIndex.PHPMaxRequests:
  83                        {
  84                            iisChangeHappened = ChangePHPMaxRequests() || iisChangeHappened;
  85                            break;
  86                        }
  87                    case PHPConfigIssueIndex.PHPRC:
  88                        {
  89                            iisChangeHappened = ChangePHPRC() || iisChangeHappened;
  90                            break;
  91                        }
  92                    case PHPConfigIssueIndex.MonitorChangesTo:
  93                        {
  94                            iisChangeHappened = ChangeMonitorChanges() || iisChangeHappened;
  95                            break;
  96                        }
  97                }
  98            }
  99            if (iisChangeHappened)
 100            {
 101                _managementUnit.Update();
 102            }
 103        }
 104
 105        private void ApplyRecommendedPHPIniSettings(ArrayList configIssueIndexes)
 106        {
 107            PHPIniFile file = new PHPIniFile(PHPIniFilePath);
 108            file.Parse();
 109
 110            List<PHPIniSetting> settings = new List<PHPIniSetting>();
 111
 112            foreach (int configIssueIndex in configIssueIndexes)
 113            {
 114                switch (configIssueIndex)
 115                {
 116                    case PHPConfigIssueIndex.ExtensionDir:
 117                        {
 118                            settings.Add(GetToApplyExtensionDir());
 119                            break;
 120                        }
 121                    case PHPConfigIssueIndex.LogErrors:
 122                        {
 123                            settings.Add(GetToApplyLogErrors());
 124                            break;
 125                        }
 126                    case PHPConfigIssueIndex.ErrorLog:
 127                        {
 128                            settings.Add(GetToApplyErrorLog(file));
 129                            break;
 130                        }
 131                    case PHPConfigIssueIndex.SessionPath:
 132                        {
 133                            settings.Add(GetToApplySessionPath(file));
 134                            break;
 135                        }
 136                    case PHPConfigIssueIndex.UploadDir:
 137                        {
 138                            settings.Add(GetToApplyUploadTmpDir(file));
 139                            break;
 140                        }
 141                    case PHPConfigIssueIndex.CgiForceRedirect:
 142                        {
 143                            settings.Add(GetToApplyCgiForceRedirect());
 144                            break;
 145                        }
 146                    case PHPConfigIssueIndex.CgiPathInfo:
 147                        {
 148                            settings.Add(GetToApplyCgiPathInfo());
 149                            break;
 150                        }
 151                    case PHPConfigIssueIndex.FastCgiImpersonation:
 152                        {
 153                            settings.Add(GetToApplyFastCgiImpersonate());
 154                            break;
 155                        }
 156                }
 157            }
 158
 159            if (settings.Count > 0)
 160            {
 161                file.AddOrUpdateSettings(settings);
 162                file.Save(PHPIniFilePath);
 163            }
 164        }
 165
 166        public void ApplyRecommendedSettings(ArrayList configIssueIndexes)
 167        {
 168            // Check if PHP is not registered
 169            if (!IsPHPRegistered())
 170            {
 171                throw new InvalidOperationException("Cannot apply recommended settings because PHP is not registered properly");
 172            }
 173
 174            ApplyRecommendedFastCgiSettings(configIssueIndexes);
 175            ApplyRecommendedPHPIniSettings(configIssueIndexes);
 176        }
 177
 178        private bool ChangeDefaultDocument()
 179        {
 180            bool changeHappened = false;
 181
 182            FileElement fileElement = _defaultDocumentCollection["index.php"];
 183            if (fileElement == null)
 184            {
 185                fileElement = _defaultDocumentCollection.CreateElement();
 186                fileElement.Value = "index.php";
 187                _defaultDocumentCollection.AddAt(0, fileElement);
 188                changeHappened = true;
 189            }
 190            else if (_defaultDocumentCollection.IndexOf(fileElement) > 0)
 191            {
 192                CopyIneritedDefaultDocs();
 193                MoveIndexPhpOnTop();
 194                changeHappened = true;
 195            }
 196
 197            return changeHappened;
 198        }
 199
 200        private bool ChangeMonitorChanges()
 201        {
 202            bool changeHappened = false;
 203
 204            // If monitorChangesTo is supported then set it
 205            if (_currentFastCgiApplication.MonitorChangesToExists())
 206            {
 207                _currentFastCgiApplication.MonitorChangesTo = PHPIniFilePath;
 208                changeHappened = true;
 209            }
 210
 211            return changeHappened;
 212        }
 213
 214        private bool ChangePHPMaxRequests()
 215        {
 216            // Set PHP_FCGI_MAX_REQUESTS to be equal to instanceMaxRequests
 217            EnvironmentVariableElement envVariableElement = _currentFastCgiApplication.EnvironmentVariables["PHP_FCGI_MAX_REQUESTS"];
 218            if (envVariableElement == null)
 219            {
 220                _currentFastCgiApplication.EnvironmentVariables.Add("PHP_FCGI_MAX_REQUESTS", _currentFastCgiApplication.InstanceMaxRequests.ToString(CultureInfo.InvariantCulture));
 221            }
 222            else
 223            {
 224                envVariableElement.Value = _currentFastCgiApplication.InstanceMaxRequests.ToString(CultureInfo.InvariantCulture);
 225            }
 226
 227            return true;
 228        }
 229
 230        private bool ChangePHPRC()
 231        {
 232            bool changeHappened = false;
 233
 234            // Set PHPRC
 235            EnvironmentVariableElement envVariableElement = _currentFastCgiApplication.EnvironmentVariables["PHPRC"];
 236            if (envVariableElement == null)
 237            {
 238                _currentFastCgiApplication.EnvironmentVariables.Add("PHPRC", PHPDirectory);
 239                changeHappened = true;
 240            }
 241            else
 242            {
 243                // If PHPRC does not point to a valid directory with php.ini ...
 244                string path = Path.Combine(envVariableElement.Value, "php.ini");
 245                if (!File.Exists(path))
 246                {
 247                    // ... then set it to current PHP directory
 248                    envVariableElement.Value = PHPDirectory;
 249                    changeHappened = true;
 250                }
 251            }
 252
 253            return changeHappened;
 254        }
 255
 256        private bool ChangeResourceType()
 257        {
 258            bool changeHappened = false;
 259
 260            if (_currentPHPHandler.ResourceType != ResourceType.Either)
 261            {
 262                _currentPHPHandler.ResourceType = ResourceType.Either;
 263                changeHappened = true;
 264            }
 265
 266            return changeHappened;
 267        }
 268
 269        private void CopyIneritedDefaultDocs()
 270        {
 271            if (_managementUnit.ConfigurationPath.PathType == ConfigurationPathType.Server)
 272            {
 273                return;
 274            }
 275
 276            FileElement[] list = new FileElement[_defaultDocumentCollection.Count];
 277            ((ICollection)_defaultDocumentCollection).CopyTo(list, 0);
 278
 279            _defaultDocumentCollection.Clear();
 280
 281            foreach (FileElement f in list)
 282            {
 283                _defaultDocumentCollection.AddCopy(f);
 284            }
 285        }
 286
 287        private void CopyInheritedHandlers()
 288        {
 289            if (_managementUnit.ConfigurationPath.PathType == ConfigurationPathType.Server)
 290            {
 291                return;
 292            }
 293
 294            HandlerElement[] list = new HandlerElement[_handlersCollection.Count];
 295            ((ICollection)_handlersCollection).CopyTo(list, 0);
 296
 297            _handlersCollection.Clear();
 298
 299            foreach (HandlerElement handler in list)
 300            {
 301                _handlersCollection.AddCopy(handler);
 302            }
 303        }
 304
 305        private static string DoubleQuotesWrap(string value)
 306        {
 307            return '"' + value + '"';
 308        }
 309
 310        private static string EnsureTrailingBackslash(string str)
 311        {            
 312            if (!str.EndsWith(@"\", StringComparison.Ordinal))
 313            {
 314                return str + @"\";
 315            }
 316            
 317            return str;
 318        }
 319
 320        private static string GenerateHandlerName(HandlersCollection collection, string phpVersion)
 321        {
 322            string prefix = "php-" + phpVersion;
 323            string name = prefix;
 324
 325            for (int i = 1; true; i++)
 326            {
 327                if (collection[name] != null)
 328                {
 329                    name = prefix + "_" + i.ToString(CultureInfo.InvariantCulture);
 330                }
 331                else
 332                {
 333                    break;
 334                }
 335            }
 336            return name;
 337        }
 338
 339        public ArrayList GetAllPHPVersions()
 340        {
 341            ArrayList result = new ArrayList();
 342            
 343            foreach (HandlerElement handler in _handlersCollection)
 344            {
 345                if (String.Equals(handler.Path, "*.php", StringComparison.OrdinalIgnoreCase))
 346                {
 347                    if (String.Equals(handler.Modules, "FastCgiModule", StringComparison.OrdinalIgnoreCase) &&
 348                        File.Exists(handler.Executable))
 349                    {
 350                        result.Add(new string[] { handler.Name, handler.Executable, GetPHPExecutableVersion(handler.Executable) });
 351                    }
 352                }
 353            }
 354
 355            return result;
 356        }
 357
 358        public PHPConfigInfo GetPHPConfigInfo()
 359        {
 360            PHPConfigInfo configInfo = new PHPConfigInfo();
 361
 362            // If PHP is not registered properly then just return information about
 363            // how it registered.
 364            if (!IsPHPRegistered())
 365            {
 366                configInfo.RegistrationType = _registrationType;
 367                return configInfo;
 368            }
 369
 370            configInfo.RegistrationType = _registrationType;
 371            configInfo.HandlerName = _currentPHPHandler.Name;
 372            configInfo.Executable = _currentPHPHandler.Executable;
 373            configInfo.Version = GetPHPExecutableVersion(_currentPHPHandler.Executable);
 374
 375            if (String.IsNullOrEmpty(PHPIniFilePath))
 376            {
 377                throw new FileNotFoundException("php.ini file does not exist");
 378            }
 379
 380            configInfo.PHPIniFilePath = PHPIniFilePath;
 381
 382            PHPIniFile file = new PHPIniFile(PHPIniFilePath);
 383            file.Parse();
 384
 385            PHPIniSetting setting = file.GetSetting("error_log");
 386            if (setting != null)
 387            {
 388                configInfo.ErrorLog = setting.TrimmedValue;
 389            }
 390            else
 391            {
 392                configInfo.ErrorLog = String.Empty;
 393            }
 394
 395            configInfo.EnabledExtCount = file.GetEnabledExtensionsCount();
 396            configInfo.InstalledExtCount = file.Extensions.Count;
 397
 398            ICollection issues = ValidateConfiguration(file);
 399            configInfo.IsConfigOptimal = (issues.Count == 0);
 400
 401            return configInfo;
 402        }
 403
 404        private string GetPHPDirectory()
 405        {
 406            string phpDirectory = Path.GetDirectoryName(Environment.ExpandEnvironmentVariables(_currentPHPHandler.Executable));
 407            return EnsureTrailingBackslash(phpDirectory);
 408        }
 409
 410        private static string GetPHPExecutableVersion(string phpexePath)
 411        {
 412            FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(phpexePath);
 413            return fileVersionInfo.ProductVersion;
 414        }
 415
 416        private string GetPHPIniFilePath()
 417        {
 418            // If PHPRC environment variable is set then use the path specified there.
 419            // Otherwise use the same path as where PHP executable is located.
 420            string directoryPath = String.Empty;
 421            EnvironmentVariableElement phpRcElement = _currentFastCgiApplication.EnvironmentVariables["PHPRC"];
 422            if (phpRcElement != null && !String.IsNullOrEmpty(phpRcElement.Value))
 423            {
 424                directoryPath = phpRcElement.Value;
 425            }
 426            else
 427            {
 428                directoryPath = Path.GetDirectoryName(_currentPHPHandler.Executable);
 429            }
 430
 431            string phpIniPath = Path.Combine(directoryPath, "php.ini");
 432
 433            if (File.Exists(phpIniPath))
 434            {
 435                return phpIniPath;
 436            }
 437
 438            return String.Empty;
 439        }
 440
 441        private static PHPIniSetting GetToApplyCgiForceRedirect()
 442        {
 443            return new PHPIniSetting("cgi.force_redirect", "0", "PHP");
 444        }
 445
 446        private static PHPIniSetting GetToApplyCgiPathInfo()
 447        {
 448            return new PHPIniSetting("cgi.fix_pathinfo", "1", "PHP");
 449        }
 450
 451        private PHPIniSetting GetToApplyErrorLog(PHPIniFile file)
 452        {
 453            string handlerName = _currentPHPHandler.Name;
 454            PHPIniSetting setting = file.GetSetting("error_log");
 455            if (setting == null || !IsAbsoluteFilePath(setting.TrimmedValue, true))
 456            {
 457                string value = Path.Combine(Environment.ExpandEnvironmentVariables(@"%WINDIR%\Temp\"), handlerName + "_errors.log");
 458                setting = new PHPIniSetting("error_log", DoubleQuotesWrap(value), "PHP");
 459            }
 460
 461            return setting;
 462        }
 463
 464        private PHPIniSetting GetToApplyExtensionDir()
 465        {
 466            string value = EnsureTrailingBackslash(Path.Combine(PHPDirectory, "ext"));
 467            return new PHPIniSetting("extension_dir", DoubleQuotesWrap(value), "PHP");
 468        }
 469
 470        private static PHPIniSetting GetToApplyFastCgiImpersonate()
 471        {
 472            return new PHPIniSetting("fastcgi.impersonate", "1", "PHP");
 473        }
 474
 475        private static PHPIniSetting GetToApplyLogErrors()
 476        {
 477            return new PHPIniSetting("log_errors", "On", "PHP");
 478        }
 479
 480        private static PHPIniSetting GetToApplySessionPath(PHPIniFile file)
 481        {
 482            PHPIniSetting setting = file.GetSetting("session.save_path");
 483            if (setting == null || !IsAbsoluteFilePath(setting.TrimmedValue, false))
 484            {
 485                string value = Environment.ExpandEnvironmentVariables(@"%WINDIR%\Temp\");
 486                setting = new PHPIniSetting("session.save_path", DoubleQuotesWrap(value), "Session");
 487            }
 488            
 489            return setting;
 490        }
 491
 492        private static PHPIniSetting GetToApplyUploadTmpDir(PHPIniFile file)
 493        {
 494            PHPIniSetting setting = file.GetSetting("upload_tmp_dir");
 495            if (setting == null || !IsAbsoluteFilePath(setting.TrimmedValue, false))
 496            {
 497                string value = Environment.ExpandEnvironmentVariables(@"%WINDIR%\Temp\");
 498                setting = new PHPIniSetting("upload_tmp_dir", DoubleQuotesWrap(value), "PHP");
 499            }
 500
 501            return setting;
 502        }
 503
 504        private void Initialize()
 505        {
 506            // Get the handlers collection
 507            ManagementConfiguration config = _managementUnit.Configuration;
 508            HandlersSection handlersSection = (HandlersSection)config.GetSection("system.webServer/handlers", typeof(HandlersSection));
 509            _handlersCollection = handlersSection.Handlers;
 510
 511            // Get the Default document collection
 512            DefaultDocumentSection defaultDocumentSection = (DefaultDocumentSection)config.GetSection("system.webServer/defaultDocument", typeof(DefaultDocumentSection));
 513            _defaultDocumentCollection = defaultDocumentSection.Files;
 514
 515            // Get the FastCgi application collection
 516            Configuration appHostConfig = _managementUnit.ServerManager.GetApplicationHostConfiguration();
 517            FastCgiSection fastCgiSection = (FastCgiSection)appHostConfig.GetSection("system.webServer/fastCgi", typeof(FastCgiSection));
 518            _fastCgiApplicationCollection = fastCgiSection.Applications;
 519
 520            // Assume by default that PHP is not registered
 521            _registrationType = PHPRegistrationType.None;
 522
 523            // Find the currently active PHP handler and FastCGI application
 524            HandlerElement handler = _handlersCollection.GetActiveHandler("*.php");
 525            if (handler != null)
 526            {
 527                if (String.Equals(handler.Modules, "FastCgiModule", StringComparison.OrdinalIgnoreCase))
 528                {
 529                    _registrationType = PHPRegistrationType.FastCgi;
 530                }
 531                else if (String.Equals(handler.Modules, "CgiModule", StringComparison.OrdinalIgnoreCase))
 532                {
 533                    _registrationType = PHPRegistrationType.Cgi;
 534                }
 535                else if (String.Equals(handler.Modules, "IsapiModule", StringComparison.OrdinalIgnoreCase))
 536                {
 537                    _registrationType = PHPRegistrationType.Isapi;
 538                }
 539
 540                if (_registrationType == PHPRegistrationType.FastCgi)
 541                {
 542                    ApplicationElement fastCgiApplication = _fastCgiApplicationCollection.GetApplication(handler.Executable, handler.Arguments);
 543                    if (fastCgiApplication != null)
 544                    {
 545                        _currentPHPHandler = handler;
 546                        _currentFastCgiApplication = fastCgiApplication;
 547                        _phpIniFilePath = GetPHPIniFilePath();
 548                        _phpDirectory = GetPHPDirectory();
 549                    }
 550                    else
 551                    {
 552                        _registrationType = PHPRegistrationType.None;
 553                    }
 554                }
 555            }
 556        }
 557
 558        private static bool IsAbsoluteFilePath(string path, bool isFile)
 559        {
 560            string directory = Environment.ExpandEnvironmentVariables(path);
 561            if (Path.IsPathRooted(path))
 562            {
 563                if (isFile)
 564                {
 565                    directory = Path.GetDirectoryName(directory);
 566                }
 567
 568                return Directory.Exists(directory);
 569            }
 570
 571            return false;
 572        }
 573
 574        private bool IsPHPRegistered()
 575        {
 576            return (_registrationType == PHPRegistrationType.FastCgi);
 577        }
 578
 579        private HandlerElement MakeHandlerActive(string handlerName)
 580        {
 581            // We have to look up the handler elements by name because we may be working
 582            // on the copy of the handlers collection.
 583            HandlerElement handlerElement = _handlersCollection[handlerName];
 584            HandlerElement activeHandlerElement = _handlersCollection[_currentPHPHandler.Name];
 585            Debug.Assert(handlerElement != null && activeHandlerElement != null);
 586
 587            int activeHandlerIndex = _handlersCollection.IndexOf(activeHandlerElement);
 588            _handlersCollection.Remove(handlerElement);
 589            return _handlersCollection.AddCopyAt(activeHandlerIndex, handlerElement);
 590        }
 591
 592        private void MakeRecommendedFastCgiChanges()
 593        {
 594            bool iisChangeHappened = false;
 595
 596            iisChangeHappened = ChangeDefaultDocument() || iisChangeHappened;
 597            iisChangeHappened = ChangeResourceType() || iisChangeHappened;
 598            iisChangeHappened = ChangePHPMaxRequests() || iisChangeHappened;
 599            iisChangeHappened = ChangePHPRC() || iisChangeHappened;
 600            iisChangeHappened = ChangeMonitorChanges() || iisChangeHappened;
 601
 602            if (iisChangeHappened)
 603            {
 604                _managementUnit.Update();
 605            }
 606        }
 607
 608        private void MakeRecommendedPHPIniChanges()
 609        {
 610            PHPIniFile file = new PHPIniFile(PHPIniFilePath);
 611            file.Parse();
 612
 613            // Set the recommended php.ini settings
 614            List<PHPIniSetting> settings = new List<PHPIniSetting>();
 615
 616            settings.Add(GetToApplyExtensionDir());
 617            settings.Add(GetToApplyLogErrors());
 618            settings.Add(GetToApplyErrorLog(file));
 619            settings.Add(GetToApplySessionPath(file));
 620            settings.Add(GetToApplyUploadTmpDir(file));
 621            settings.Add(GetToApplyCgiForceRedirect());
 622            settings.Add(GetToApplyCgiPathInfo());
 623            settings.Add(GetToApplyFastCgiImpersonate());
 624            
 625            settings.Add(new PHPIniSetting("fastcgi.logging", "0", "PHP"));
 626            settings.Add(new PHPIniSetting("max_execution_time", "300", "PHP"));
 627            settings.Add(new PHPIniSetting("display_errors", "Off", "PHP"));
 628
 629            // Enable the most common PHP extensions
 630            List<PHPIniExtension> extensions = new List<PHPIniExtension>();
 631            extensions.Add(new PHPIniExtension("php_curl.dll", true));
 632            extensions.Add(new PHPIniExtension("php_gd2.dll", true));
 633            extensions.Add(new PHPIniExtension("php_gettext.dll", true));
 634            extensions.Add(new PHPIniExtension("php_mysql.dll", true));
 635            extensions.Add(new PHPIniExtension("php_mysqli.dll", true));
 636            extensions.Add(new PHPIniExtension("php_mbstring.dll", true));
 637            extensions.Add(new PHPIniExtension("php_openssl.dll", true));
 638            extensions.Add(new PHPIniExtension("php_soap.dll", true));
 639            extensions.Add(new PHPIniExtension("php_xmlrpc.dll", true));
 640
 641            file.UpdateExtensions(extensions);
 642            file.AddOrUpdateSettings(settings);
 643            file.Save(PHPIniFilePath);
 644        }
 645
 646        private FileElement MoveIndexPhpOnTop()
 647        {
 648            FileElement fileElement = _defaultDocumentCollection["index.php"];
 649            Debug.Assert(fileElement != null);
 650
 651            _defaultDocumentCollection.Remove(fileElement);
 652            return _defaultDocumentCollection.AddCopyAt(0, fileElement);
 653        }
 654
 655        private static string PreparePHPIniFile(string phpDir)
 656        {
 657            // Check for existence of php.ini file. If it does not exist then copy php.ini-recommended
 658            // or php.ini-production to it
 659            string phpIniFilePath = Path.Combine(phpDir, "php.ini");
 660            if (!File.Exists(phpIniFilePath))
 661            {
 662                string phpIniRecommendedPath = Path.Combine(phpDir, "php.ini-recommended");
 663                string phpIniProductionPath = Path.Combine(phpDir, "php.ini-production");
 664                if (File.Exists(phpIniRecommendedPath))
 665                {
 666                    File.Copy(phpIniRecommendedPath, phpIniFilePath);
 667                }
 668                else if (File.Exists(phpIniProductionPath))
 669                {
 670                    File.Copy(phpIniProductionPath, phpIniFilePath);
 671                }
 672                else
 673                {
 674                    throw new FileNotFoundException("php.ini and php.ini recommended do not exist in " + phpDir);
 675                }
 676            }
 677            return phpIniFilePath;
 678        }
 679
 680        public void RegisterPHPWithIIS(string path)
 681        {
 682            string phpexePath = Environment.ExpandEnvironmentVariables(path);
 683            
 684            if (!String.Equals(Path.GetFileName(phpexePath), "php-cgi.exe", StringComparison.OrdinalIgnoreCase) &&
 685                !String.Equals(Path.GetFileName(phpexePath), "php.exe", StringComparison.OrdinalIgnoreCase))
 686            {
 687                throw new ArgumentException("The provided php executable path is invalid", phpexePath);
 688            }
 689
 690            // Check for existence of php executable in the specified directory
 691            if (!File.Exists(phpexePath))
 692            {
 693                throw new FileNotFoundException("php-cgi.exe and php.exe do not exist");
 694            }
 695
 696            // Check for existence of php extensions directory
 697            string phpDir = EnsureTrailingBackslash(Path.GetDirectoryName(phpexePath));
 698            string extDir = Path.Combine(phpDir, "ext");
 699            if (!Directory.Exists(extDir))
 700            {
 701                throw new DirectoryNotFoundException("ext directory does not exist in " + phpDir);
 702            }
 703
 704            string phpIniFilePath = PreparePHPIniFile(phpDir);
 705
 706            bool iisUpdateHappened = false;
 707            ApplicationElement fastCgiApplication = _fastCgiApplicationCollection.GetApplication(phpexePath, "");
 708            // Create a FastCGI application if it does not exist
 709            bool isNewFastCgi = false;
 710            if (fastCgiApplication == null)
 711            {
 712                fastCgiApplication = _fastCgiApplicationCollection.CreateElement();
 713                fastCgiApplication.FullPath = phpexePath;
 714                // monitorChangesTo may not exist if FastCGI update is not installed
 715                if (fastCgiApplication.MonitorChangesToExists())
 716                {
 717                    fastCgiApplication.MonitorChangesTo = phpIniFilePath;
 718                }
 719                fastCgiApplication.InstanceMaxRequests = 10000;
 720                fastCgiApplication.ActivityTimeout = 300;
 721                fastCgiApplication.RequestTimeout = 300;
 722
 723                fastCgiApplication.EnvironmentVariables.Add("PHPRC", phpDir);
 724                fastCgiApplication.EnvironmentVariables.Add("PHP_FCGI_MAX_REQUESTS", "10000");
 725
 726                _fastCgiApplicationCollection.Add(fastCgiApplication);
 727                isNewFastCgi = true;
 728                iisUpdateHappened = true;
 729            }
 730                
 731            // Check if handler mapping with this executable already exists
 732            HandlerElement handlerElement = _handlersCollection.GetHandler("*.php", phpexePath);
 733            // Create a handler mapping if it does not exist
 734            bool isNewHandler = false;
 735            if (handlerElement == null)
 736            {
 737                // Create a PHP file handler if it does not exist
 738                handlerElement = _handlersCollection.CreateElement();
 739                handlerElement.Name = GenerateHandlerName(_handlersCollection, GetPHPExecutableVersion(phpexePath));
 740                handlerElement.Modules = "FastCgiModule";
 741                handlerElement.RequireAccess = RequireAccess.Script;
 742                handlerElement.Verb = "GET,HEAD,POST";
 743                handlerElement.Path = "*.php";
 744                handlerElement.ScriptProcessor = phpexePath;
 745                handlerElement.ResourceType = ResourceType.Either;
 746                _handlersCollection.AddAt(0, handlerElement);
 747                isNewHandler = true;
 748                iisUpdateHappened = true;
 749            }
 750            else if (_currentPHPHandler != null && handlerElement != _currentPHPHandler)
 751            {
 752                // Move the existing PHP file handler mapping on top
 753                CopyInheritedHandlers();
 754                MakeHandlerActive(handlerElement.Name);
 755                iisUpdateHappened = true;
 756            }
 757
 758            // Check if index.php is set as a default document and move it to the top of the list
 759            iisUpdateHappened = ChangeDefaultDocument() || iisUpdateHappened;
 760
 761            if (iisUpdateHappened)
 762            {
 763                _managementUnit.Update();
 764                // We need to call Initialize() again to set references to current handler and 
 765                // fastcgi application and to avoid the read-only exception from IIS config
 766                Initialize();
 767            }
 768
 769            // Make the recommended changes to php.ini file
 770            MakeRecommendedPHPIniChanges();
 771
 772            // Make recommended changes to existing iis configuration. This is the case
 773            // when either FastCGI application or a handler mapping or both existed for the
 774            // specified php-cgi.exe executable.
 775            if (!isNewFastCgi || !isNewHandler)
 776            {
 777                MakeRecommendedFastCgiChanges();
 778            }
 779        }
 780
 781        public void SelectPHPHandler(string name)
 782        {
 783            // PHP is not registered properly so we don't attempt to do anything.
 784            if (!IsPHPRegistered())
 785            {
 786                return;
 787            }
 788
 789            HandlerElement handler = _handlersCollection[name];
 790            // If the handler is already an active PHP handler then no need to do anything.
 791            if (handler != null && handler != _currentPHPHandler)
 792            {
 793                CopyInheritedHandlers();
 794                MakeHandlerActive(name);
 795                _managementUnit.Update();
 796            }
 797        }
 798
 799        private static PHPConfigIssue ValidateCgiForceRedirect(PHPIniFile file)
 800        {
 801            PHPConfigIssue configIssue = null;
 802            
 803            // Check if cgi.force_redirect is set correctly
 804            PHPIniSetting setting = file.GetSetting("cgi.force_redirect");
 805            if (setting == null || String.IsNullOrEmpty(setting.TrimmedValue))
 806            {
 807                configIssue = new PHPConfigIssue("cgi.force_redirect",
 808                                                                String.Empty,
 809                                                                "0",
 810                                                                "ConfigIssueCgiForceRedirectNotSet",
 811                                                                "ConfigIssueCgiForceRedirectRecommend",
 812                                                                PHPConfigIssueIndex.CgiForceRedirect);
 813            }
 814            else if (!String.Equals(setting.TrimmedValue, "0", StringComparison.OrdinalIgnoreCase))
 815            {
 816                configIssue = new PHPConfigIssue("cgi.force_redirect",
 817                                                                setting.TrimmedValue,
 818                                                                "0",
 819                                                                "ConfigIssueCgiForceRedirectNotCorrect",
 820                                                                "ConfigIssueCgiForceRedirectRecommend",
 821                                                                PHPConfigIssueIndex.CgiForceRedirect);
 822            }
 823
 824            return configIssue;
 825        }
 826
 827        private static PHPConfigIssue ValidateCgiPathInfo(PHPIniFile file)
 828        {
 829            PHPConfigIssue configIssue = null;
 830            
 831            // Check if cgi.fix_pathinfo is set correctly
 832            PHPIniSetting setting = file.GetSetting("cgi.fix_pathinfo");
 833            if (setting == null || String.IsNullOrEmpty(setting.TrimmedValue))
 834            {
 835                configIssue = new PHPConfigIssue("cgi.fix_pathinfo",
 836                                                                String.Empty,
 837                                                                "1",
 838                                                                "ConfigIssueCgiPathInfoNotSet",
 839                                                                "ConfigIssueCgiPathInfoRecommend",
 840                                                                PHPConfigIssueIndex.CgiPathInfo);
 841            }
 842            else if (!String.Equals(setting.TrimmedValue, "1", StringComparison.OrdinalIgnoreCase))
 843            {
 844                configIssue = new PHPConfigIssue("cgi.fix_pathinfo",
 845                                                                setting.TrimmedValue,
 846                                                                "1",
 847                                                                "ConfigIssueCgiPathInfoNotCorrect",
 848                                                                "ConfigIssueCgiPathInfoRecommend",
 849                                                                PHPConfigIssueIndex.CgiPathInfo);
 850            }
 851
 852            return configIssue;
 853        }
 854
 855        public RemoteObjectCollection<PHPConfigIssue> ValidateConfiguration()
 856        {
 857            // Check if PHP is not registered
 858            if (!IsPHPRegistered())
 859            {
 860                return null;
 861            }
 862
 863            PHPIniFile file = new PHPIniFile(PHPIniFilePath);
 864            file.Parse();
 865
 866            return ValidateConfiguration(file);
 867        }
 868
 869        private RemoteObjectCollection<PHPConfigIssue> ValidateConfiguration(PHPIniFile file)
 870        {
 871
 872            RemoteObjectCollection<PHPConfigIssue> configIssues = new RemoteObjectCollection<PHPConfigIssue>();
 873          
 874            // IIS and FastCGI settings
 875            PHPConfigIssue configIssue = ValidateDefaultDocument();
 876            if (configIssue != null)
 877            {
 878                configIssues.Add(configIssue);
 879            }
 880            
 881            configIssue = ValidateResourceType();
 882            if (configIssue != null)
 883            {
 884                configIssues.Add(configIssue);
 885            }
 886
 887            configIssue = ValidatePHPMaxRequests();
 888            if (configIssue != null)
 889            {
 890                configIssues.Add(configIssue);
 891            }
 892
 893            configIssue = ValidatePHPRC();
 894            if (configIssue != null)
 895            {
 896                configIssues.Add(configIssue);
 897            }
 898
 899            configIssue = ValidateMonitorChanges();
 900            if (configIssue != null)
 901            {
 902                configIssues.Add(configIssue);
 903            }
 904
 905            // PHP Settings
 906            configIssue = ValidateExtensionDir(file);
 907            if (configIssue != null)
 908            {
 909                configIssues.Add(configIssue);
 910            }
 911
 912            configIssue = ValidateLogErrors(file);
 913            if (configIssue != null)
 914            {
 915                configIssues.Add(configIssue);
 916            }
 917
 918            configIssue = ValidateErrorLog(file);
 919            if (configIssue != null)
 920            {
 921                configIssues.Add(configIssue);
 922            }
 923
 924            configIssue = ValidateSessionPath(file);
 925            if (configIssue != null)
 926            {
 927                configIssues.Add(configIssue);
 928            }
 929
 930            configIssue = ValidateUploadTmpDir(file);
 931            if (configIssue != null)
 932            {
 933                configIssues.Add(configIssue);
 934            }
 935
 936            configIssue = ValidateCgiForceRedirect(file);
 937            if (configIssue != null)
 938            {
 939                configIssues.Add(configIssue);
 940            }
 941
 942            configIssue = ValidateCgiPathInfo(file);
 943            if (configIssue != null)
 944            {
 945                configIssues.Add(configIssue);
 946            }
 947
 948            configIssue = ValidateFastCgiImpersonate(file);
 949            if (configIssue != null)
 950            {
 951                configIssues.Add(configIssue);
 952            }
 953
 954            return configIssues;
 955        }
 956
 957        private PHPConfigIssue ValidateDefaultDocument()
 958        {
 959            PHPConfigIssue configIssue = null;
 960            // Check if index.php is set as a default document
 961            FileElement fileElement = _defaultDocumentCollection["index.php"];
 962            if (fileElement == null)
 963            {
 964                configIssue = new PHPConfigIssue("Default document",
 965                                                                _defaultDocumentCollection[0].Value,
 966                                                                "index.php",
 967                                                                "ConfigIssueDefaultDocumentNotSet",
 968                                                                "ConfigIssueDefaultDocumentRecommend",
 969                                                                PHPConfigIssueIndex.DefaultDocument);
 970            }
 971            else if (_defaultDocumentCollection.IndexOf(fileElement) > 0)
 972            {
 973                configIssue = new PHPConfigIssue("Default document",
 974                                                                _defaultDocumentCollection[0].Value,
 975                                                                "index.php",
 976                                                                "ConfigIssueDefaultDocumentNotFirst",
 977                                                                "ConfigIssueDefaultDocumentRecommend",
 978                                                                PHPConfigIssueIndex.DefaultDocument);
 979            }
 980            
 981            return configIssue;
 982        }
 983
 984        private PHPConfigIssue ValidateErrorLog(PHPIniFile file)
 985        {
 986            PHPConfigIssue configIssue = null;
 987
 988            // Check if error_log is set to an absolute path and that path exists
 989            PHPIniSetting setting = file.GetSetting("error_log");
 990            string expectedValue = Path.Combine(Environment.ExpandEnvironmentVariables(@"%WINDIR%\Temp\"), _currentPHPHandler.Name + "_errors.log");
 991            if (setting == null || String.IsNullOrEmpty(setting.TrimmedValue))
 992            {
 993                configIssue = new PHPConfigIssue("error_log",
 994                                                                String.Empty,
 995                                                                expectedValue,
 996                                                                "ConfigIssueErrorLogNotSet",
 997                                                                "ConfigIssueErrorLogRecommend",
 998                                                                PHPConfigIssueIndex.ErrorLog);
 999            }
1000            else if (!IsAbsoluteFilePath(setting.TrimmedValue, true /* this is supposed to be a file */))
1001            {
1002                configIssue = new PHPConfigIssue("error_log",
1003                                                                setting.TrimmedValue,
1004                                                                expectedValue,
1005                                                                "ConfigIssueErrorLogNotCorrect",
1006                                                                "ConfigIssueErrorLogRecommend",
1007                                                                PHPConfigIssueIndex.ErrorLog);
1008            }
1009
1010            return configIssue;
1011        }
1012
1013        private PHPConfigIssue ValidateExtensionDir(PHPIniFile file)
1014        {
1015            PHPConfigIssue configIssue = null;
1016            
1017            PHPIniSetting setting = file.GetSetting("extension_dir");
1018            string expectedValue = EnsureTrailingBackslash(Path.Combine(PHPDirectory, "ext"));
1019            if (setting == null || String.IsNullOrEmpty(setting.TrimmedValue))
1020            {
1021                configIssue = new PHPConfigIssue("extension_dir",
1022                                                                String.Empty,
1023                                                                expectedValue,
1024                                                                "ConfigIssueExtensionDirNotSet",
1025                                                                "ConfigIssueExtensionDirRecommend",
1026                                                                PHPConfigIssueIndex.ExtensionDir);
1027            }
1028            else
1029            {
1030                string currentValue = EnsureTrailingBackslash(setting.TrimmedValue);
1031                if (!String.Equals(currentValue, expectedValue, StringComparison.OrdinalIgnoreCase))
1032                {
1033                    configIssue = new PHPConfigIssue("extension_dir",
1034                                                                    setting.TrimmedValue,
1035                                                                    expectedValue,
1036                                                                    "ConfigIssueExtensionDirIncorrect",
1037                                                                    "ConfigIssueExtensionDirRecommend",
1038                                                                    PHPConfigIssueIndex.ExtensionDir);
1039                }
1040            }
1041
1042            return configIssue;
1043        }
1044
1045        private static PHPConfigIssue ValidateFastCgiImpersonate(PHPIniFile file)
1046        {
1047            PHPConfigIssue configIssue = null;
1048            
1049            // Check if fastcgi impersonation is turned on
1050            PHPIniSetting setting = file.GetSetting("fastcgi.impersonate");
1051            if (setting == null || String.IsNullOrEmpty(setting.TrimmedValue))
1052            {
1053                configIssue = new PHPConfigIssue("fastcgi.impersonate",
1054                                                                String.Empty,
1055                                                                "1",
1056                                                                "ConfigIssueFastCgiImpersonateNotSet",
1057                                                                "ConfigIssueFastCgiImpersonateRecommend",
1058                                                                PHPConfigIssueIndex.FastCgiImpersonation);
1059            }
1060            else if (!String.Equals(setting.TrimmedValue, "1", StringComparison.OrdinalIgnoreCase))
1061            {
1062                configIssue = new PHPConfigIssue("fastcgi.impersonate",
1063                                                                setting.TrimmedValue,
1064                                                                "1",
1065                                                                "ConfigIssueFastCgiImpersonateNotCorrect",
1066                                                                "ConfigIssueFastCgiImpersonateRecommend",
1067                                                                PHPConfigIssueIndex.FastCgiImpersonation);
1068            }
1069
1070            return configIssue;
1071        }
1072
1073        private static PHPConfigIssue ValidateLogErrors(PHPIniFile file)
1074        {
1075            PHPConfigIssue configIssue = null;
1076
1077            // Check if log_errors is set to On
1078            PHPIniSetting setting = file.GetSetting("log_errors");
1079            if (setting == null || String.IsNullOrEmpty(setting.TrimmedValue))
1080            {
1081                configIssue = new PHPConfigIssue("log_errors",
1082                                                                String.Empty,
1083                                                                "On",
1084                                                                "ConfigIssueLogErrorsNotSet",
1085                                                                "ConfigIssueLogErrorsRecommend",
1086                                                                PHPConfigIssueIndex.LogErrors);
1087            }
1088            else if (!String.Equals(setting.TrimmedValue, "On", StringComparison.OrdinalIgnoreCase))
1089            {
1090                configIssue = new PHPConfigIssue("log_errors",
1091                                                                setting.TrimmedValue,
1092                                                                "On",
1093                                                                "ConfigIssueLogErrorsNotCorrect",
1094                                                                "ConfigIssueLogErrorsRecommend",
1095                                                                PHPConfigIssueIndex.LogErrors);
1096            }
1097
1098            return configIssue;
1099        }
1100
1101        private PHPConfigIssue ValidateMonitorChanges()
1102        {
1103            PHPConfigIssue configIssue = null;
1104            // Check if monitorChangesTo setting is supported and is set correctly
1105            if (_currentFastCgiApplication.MonitorChangesToExists())
1106            {
1107                string path = _currentFastCgiApplication.MonitorChangesTo;
1108                if (String.IsNullOrEmpty(path))
1109                {
1110                    configIssue = new PHPConfigIssue("monitorChangesTo",
1111                                                                    String.Empty,
1112                                                                    PHPIniFilePath,
1113                                                                    "ConfigIssueMonitorChangesNotSet",
1114                                                                    "ConfigIssueMonitorChangesRecommend",
1115                                                                    PHPConfigIssueIndex.MonitorChangesTo);
1116                }
1117                else if (!String.Equals(PHPIniFilePath, path, StringComparison.OrdinalIgnoreCase))
1118                {
1119                    configIssue = new PHPConfigIssue("monitorChangesTo",
1120                                                                    path,
1121                                                                    PHPIniFilePath,
1122                                                                    "ConfigIssueMonitorChangesIncorrect",
1123                                                                    "ConfigIssueMonitorChangesRecommend",
1124                                                                    PHPConfigIssueIndex.MonitorChangesTo);
1125                }
1126            }
1127
1128            return configIssue;
1129        }
1130
1131        private PHPConfigIssue ValidatePHPMaxRequests()
1132        {
1133            PHPConfigIssue configIssue = null;
1134            // Check if PHP_FCGI_MAX_REQUESTS is set and is bigger than instanceMaxRequests
1135            EnvironmentVariableElement envVariableElement = _currentFastCgiApplication.EnvironmentVariables["PHP_FCGI_MAX_REQUESTS"];
1136            if (envVariableElement == null)
1137            {
1138                configIssue = new PHPConfigIssue("PHP_FCGI_MAX_REQUESTS",
1139                                                                String.Empty,
1140                                                                _currentFastCgiApplication.InstanceMaxRequests.ToString(CultureInfo.InvariantCulture),
1141                                                                "ConfigIssuePHPMaxRequestsNotSet",
1142                                                                "ConfigIssuePHPMaxRequestsRecommend",
1143                                                                PHPConfigIssueIndex.PHPMaxRequests);
1144            }
1145            else
1146            {
1147                long maxRequests;
1148                if (!Int64.TryParse(envVariableElement.Value, out maxRequests) ||
1149                    (maxRequests < _currentFastCgiApplication.InstanceMaxRequests))
1150                {
1151                    configIssue = new PHPConfigIssue("PHP_FCGI_MAX_REQUESTS",
1152                                                                    envVariableElement.Value,
1153                                                                    _currentFastCgiApplication.InstanceMaxRequests.ToString(CultureInfo.InvariantCulture),
1154                                                                    "ConfigIssuePHPMaxRequestsIncorrect",
1155             

Large files files are truncated, but you can click here to view the full file