├── vendor ├── composer │ ├── installed.json │ ├── autoload_namespaces.php │ ├── autoload_psr4.php │ ├── autoload_files.php │ ├── autoload_classmap.php │ ├── LICENSE │ ├── autoload_static.php │ ├── autoload_real.php │ └── ClassLoader.php └── autoload.php ├── src ├── pre_defined.php ├── Support │ ├── Setting.php │ ├── Console.php │ ├── Application.php │ ├── Installer.php │ ├── VersionRepository.php │ └── Handler.php ├── xphp.php ├── path_register.vbs ├── need_standardize.lst ├── Templates │ └── xampp_config │ │ ├── httpd-xampp-php5.conf.tpl │ │ ├── httpd-xampp-php7.conf.tpl │ │ └── httpd-xampp-php8.conf.tpl ├── power_exec.vbs └── helpers.php ├── .gitignore ├── composer.json ├── LICENSE ├── xphp.hlp ├── xphp.bat └── README.md /vendor/composer/installed.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /src/pre_defined.php: -------------------------------------------------------------------------------- 1 | array($baseDir . '/src'), 10 | ); 11 | -------------------------------------------------------------------------------- /vendor/composer/autoload_files.php: -------------------------------------------------------------------------------- 1 | $baseDir . '/src/pre_defined.php', 10 | '797a28cf5fafaaecbe83f3fd6d91719e' => $baseDir . '/src/helpers.php', 11 | ); 12 | -------------------------------------------------------------------------------- /vendor/composer/autoload_classmap.php: -------------------------------------------------------------------------------- 1 | $baseDir . '/src/Support/Application.php', 10 | 'PHPSwitcher\\Support\\Console' => $baseDir . '/src/Support/Console.php', 11 | 'PHPSwitcher\\Support\\Handler' => $baseDir . '/src/Support/Handler.php', 12 | 'PHPSwitcher\\Support\\Installer' => $baseDir . '/src/Support/Installer.php', 13 | 'PHPSwitcher\\Support\\Setting' => $baseDir . '/src/Support/Setting.php', 14 | 'PHPSwitcher\\Support\\VersionRepository' => $baseDir . '/src/Support/VersionRepository.php', 15 | ); 16 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jackiedo/xampp-php-switcher", 3 | "description": "Allow to use and switch between different versions of PHP for Xampp on Windows OS.", 4 | "keywords": [ 5 | "xampp", 6 | "xampp-windows", 7 | "php", 8 | "php-switcher", 9 | "php-switch", 10 | "console" 11 | ], 12 | "license": "MIT", 13 | "type": "project", 14 | "require": { 15 | "php": ">=5.4" 16 | }, 17 | "autoload": { 18 | "files": [ 19 | "src/pre_defined.php", 20 | "src/helpers.php" 21 | ], 22 | "psr-4": { 23 | "PHPSwitcher\\": "src/" 24 | } 25 | }, 26 | "scripts": { 27 | "post-create-project-cmd": [ 28 | "xphp.bat install" 29 | ] 30 | }, 31 | "config": { 32 | "sort-packages": true, 33 | "optimize-autoloader": true 34 | } 35 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Anh Vũ Đỗ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/composer/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) Nils Adermann, Jordi Boggiano 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is furnished 9 | to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /xphp.hlp: -------------------------------------------------------------------------------- 1 | Switches between PHP versions for Xampp on Windows OS 2 | ----------------------------------------------------- 3 | 4 | Usage: 5 | xphp [parameter] 6 | 7 | Available commands: 8 | install Intergrate Xampp PHP Switcher into Xampp 9 | (require running in Administrator mode). 10 | list List all PHP builds in repository. 11 | add Add new PHP build into repository. 12 | remove Remove specific PHP build in repository. 13 | info Show information about specific PHP build. 14 | switch Switch to specific PHP version. 15 | help Display the help message. 16 | 17 | Notes: 18 | - Depending on the argument, the [parameter] 19 | argument can be a version number or a path to a PHP build. 20 | This argument is optional, if not passed, it will also be 21 | asked if needed. 22 | 23 | Examples: 24 | xphp info current 25 | - Will show information about the current PHP build. 26 | 27 | xphp info 7.1.9 28 | - Will show information about the PHP build that has 29 | version "7.1.9" 30 | 31 | xphp info 32 | - Will be asked to choose one version from the list 33 | to view detail information. 34 | 35 | xphp switch 36 | - Will be asked to choose one version from the list 37 | to switching. 38 | 39 | xphp add "X:\path\to\another\xampp-php-directory" 40 | - Will add another compatible PHP build into repository. 41 | -------------------------------------------------------------------------------- /src/Support/Setting.php: -------------------------------------------------------------------------------- 1 | reloadSettings(); 12 | } 13 | 14 | public function reloadSettings() 15 | { 16 | if (is_file(getenv('XPHP_APP_DIR') . '\settings.ini')) { 17 | $this->settings = @parse_ini_file(getenv('XPHP_APP_DIR') . '\settings.ini', true); 18 | } 19 | 20 | return $this; 21 | } 22 | 23 | public function all() 24 | { 25 | return $this->settings; 26 | } 27 | 28 | public function get($section, $setting, $defaultValue = null) 29 | { 30 | if (array_key_exists($section, $this->settings)) { 31 | $returnValue = (array_key_exists($setting, $this->settings[$section])) ? $this->settings[$section][$setting] : $defaultValue; 32 | } else { 33 | $returnValue = $defaultValue; 34 | } 35 | 36 | return $returnValue; 37 | } 38 | 39 | public function set($section, $setting, $value) 40 | { 41 | if (! array_key_exists($section, $this->settings)) { 42 | $this->settings[$section] = []; 43 | } 44 | 45 | $this->settings[$section][$setting] = $value; 46 | 47 | return $this; 48 | } 49 | 50 | public function save() 51 | { 52 | return @create_ini_file(getenv('XPHP_APP_DIR') . '\settings.ini', $this->settings, true); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /vendor/composer/autoload_static.php: -------------------------------------------------------------------------------- 1 | __DIR__ . '/../..' . '/src/pre_defined.php', 11 | '797a28cf5fafaaecbe83f3fd6d91719e' => __DIR__ . '/../..' . '/src/helpers.php', 12 | ); 13 | 14 | public static $prefixLengthsPsr4 = array ( 15 | 'P' => 16 | array ( 17 | 'PHPSwitcher\\' => 12, 18 | ), 19 | ); 20 | 21 | public static $prefixDirsPsr4 = array ( 22 | 'PHPSwitcher\\' => 23 | array ( 24 | 0 => __DIR__ . '/../..' . '/src', 25 | ), 26 | ); 27 | 28 | public static $classMap = array ( 29 | 'PHPSwitcher\\Support\\Application' => __DIR__ . '/../..' . '/src/Support/Application.php', 30 | 'PHPSwitcher\\Support\\Console' => __DIR__ . '/../..' . '/src/Support/Console.php', 31 | 'PHPSwitcher\\Support\\Handler' => __DIR__ . '/../..' . '/src/Support/Handler.php', 32 | 'PHPSwitcher\\Support\\Installer' => __DIR__ . '/../..' . '/src/Support/Installer.php', 33 | 'PHPSwitcher\\Support\\Setting' => __DIR__ . '/../..' . '/src/Support/Setting.php', 34 | 'PHPSwitcher\\Support\\VersionRepository' => __DIR__ . '/../..' . '/src/Support/VersionRepository.php', 35 | ); 36 | 37 | public static function getInitializer(ClassLoader $loader) 38 | { 39 | return \Closure::bind(function () use ($loader) { 40 | $loader->prefixLengthsPsr4 = ComposerStaticInit055b685dedcc6004365c765297bf414d::$prefixLengthsPsr4; 41 | $loader->prefixDirsPsr4 = ComposerStaticInit055b685dedcc6004365c765297bf414d::$prefixDirsPsr4; 42 | $loader->classMap = ComposerStaticInit055b685dedcc6004365c765297bf414d::$classMap; 43 | 44 | }, null, ClassLoader::class); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/xphp.php: -------------------------------------------------------------------------------- 1 | #" . PHP_EOL 25 | . "# License: MIT (c) #" . PHP_EOL 26 | . "###################################################################################" . PHP_EOL; 27 | 28 | if (isset($_SERVER['argv'][1])) { 29 | Console::line($banner); 30 | 31 | if ($_SERVER['argv'][1] == 'install') { 32 | $installer = new Installer; 33 | $installer->install(); 34 | Console::terminate(null, 0, true); 35 | } 36 | 37 | $witcher = new Handler; 38 | 39 | switch ($_SERVER['argv'][1]) { 40 | case 'listVersions': 41 | $witcher->listVersions(); 42 | break; 43 | 44 | case 'addVersion': 45 | $witcher->addVersion($_SERVER['argv'][2]); 46 | break; 47 | 48 | case 'removeVersion': 49 | $witcher->removeVersion($_SERVER['argv'][2]); 50 | break; 51 | 52 | case 'showVersion': 53 | $witcher->showVersion($_SERVER['argv'][2]); 54 | break; 55 | 56 | case 'switchVersion': 57 | $witcher->switchVersion($_SERVER['argv'][2]); 58 | break; 59 | 60 | default: 61 | break; 62 | } 63 | 64 | Console::terminate(null, 0, true); 65 | } 66 | 67 | Console::terminate(null, 0, true); 68 | -------------------------------------------------------------------------------- /src/path_register.vbs: -------------------------------------------------------------------------------- 1 | Option Explicit 2 | 3 | ' Define global variables 4 | Dim objArgs: Set objArgs = WScript.Arguments 5 | Dim objShell: Set objShell = CreateObject("WScript.Shell") 6 | Dim objSystemEnv: Set objSystemEnv = objShell.Environment("SYSTEM") 7 | 8 | ' Define subroutines 9 | Private Sub ClearVars() 10 | Set objSystemEnv = Nothing 11 | Set objShell = Nothing 12 | Set objArgs = Nothing 13 | End Sub 14 | 15 | Private Sub WriteMsg(ByVal message, ByVal writeAs) 16 | If (DetectScriptMode = "Window") Then 17 | MsgBox message 18 | Else 19 | Select Case writeAs 20 | Case "output" 21 | WScript.StdOut.WriteLine message 22 | Case "error" 23 | WScript.StdErr.WriteLine message 24 | Case "echo" 25 | WScript.Echo message 26 | End Select 27 | End If 28 | End Sub 29 | 30 | Private Sub Quit(errorCode) 31 | ClearVars 32 | WScript.Quit errorCode 33 | End Sub 34 | 35 | ' Define functions 36 | Private Function DetectScriptMode() 37 | Dim scriptMode 38 | 39 | If InStr(1, WScript.FullName, "cscript", vbTextCompare) Then 40 | scriptMode = "Console" 41 | ElseIf InStr(1, WScript.FullName, "wscript", vbTextCompare) Then 42 | scriptMode = "Window" 43 | Else 44 | scriptMode = "Unknown" 45 | End If 46 | 47 | DetectScriptMode = scriptMode 48 | End Function 49 | 50 | ' Check Elevated permission 51 | Dim objExec: Set objExec = objShell.Exec("cmd /C ""FSUTIL dirty query %SystemDrive%>nul""") 52 | Do While (objExec.Status = 0) 53 | WScript.Sleep 50 54 | Loop 55 | If (objExec.ExitCode = 1) Then 56 | WriteMsg "This process can only be run with elevated permission.", "error" 57 | WriteMsg "Please run this process in Administartor mode.", "error" 58 | Set objExec = Nothing 59 | Quit 1 60 | End If 61 | Set objExec = Nothing 62 | 63 | ' The path argument is missing 64 | If (objArgs.Count <= 0) Then 65 | WriteMsg "The ""path"" argument is missing.", "error" 66 | Quit 1 67 | End If 68 | 69 | ' Start process 70 | Dim currentPaths: currentPaths = objSystemEnv("PATH") 71 | Dim path: path = Trim(objArgs.item(0)) 72 | 73 | If (InStr(currentPaths, path & ";") = 0) and (InStr(currentPaths, ";" & path) = 0) Then 74 | objSystemEnv("PATH") = path & ";" & currentPaths 75 | End If 76 | 77 | WriteMsg "The path has been added into Windows Path Environment Variable", "output" 78 | Quit 0 -------------------------------------------------------------------------------- /vendor/composer/autoload_real.php: -------------------------------------------------------------------------------- 1 | = 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); 30 | if ($useStaticLoader) { 31 | require_once __DIR__ . '/autoload_static.php'; 32 | 33 | call_user_func(\Composer\Autoload\ComposerStaticInit055b685dedcc6004365c765297bf414d::getInitializer($loader)); 34 | } else { 35 | $map = require __DIR__ . '/autoload_namespaces.php'; 36 | foreach ($map as $namespace => $path) { 37 | $loader->set($namespace, $path); 38 | } 39 | 40 | $map = require __DIR__ . '/autoload_psr4.php'; 41 | foreach ($map as $namespace => $path) { 42 | $loader->setPsr4($namespace, $path); 43 | } 44 | 45 | $classMap = require __DIR__ . '/autoload_classmap.php'; 46 | if ($classMap) { 47 | $loader->addClassMap($classMap); 48 | } 49 | } 50 | 51 | $loader->register(true); 52 | 53 | if ($useStaticLoader) { 54 | $includeFiles = Composer\Autoload\ComposerStaticInit055b685dedcc6004365c765297bf414d::$files; 55 | } else { 56 | $includeFiles = require __DIR__ . '/autoload_files.php'; 57 | } 58 | foreach ($includeFiles as $fileIdentifier => $file) { 59 | composerRequire055b685dedcc6004365c765297bf414d($fileIdentifier, $file); 60 | } 61 | 62 | return $loader; 63 | } 64 | } 65 | 66 | function composerRequire055b685dedcc6004365c765297bf414d($fileIdentifier, $file) 67 | { 68 | if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { 69 | require $file; 70 | 71 | $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/need_standardize.lst: -------------------------------------------------------------------------------- 1 | dbunit 2 | dbunit.bat 3 | pci 4 | pci.bat 5 | pciconf 6 | pciconf.bat 7 | pear.bat 8 | pear\.registry\archive_tar.reg 9 | pear\.registry\auth.reg 10 | pear\.registry\auth_sasl.reg 11 | pear\.registry\benchmark.reg 12 | pear\.registry\cache.reg 13 | pear\.registry\cache_lite.reg 14 | pear\.registry\config.reg 15 | pear\.registry\console_color.reg 16 | pear\.registry\console_commandline.reg 17 | pear\.registry\console_getargs.reg 18 | pear\.registry\console_getopt.reg 19 | pear\.registry\console_table.reg 20 | pear\.registry\crypt_chap.reg 21 | pear\.registry\date.reg 22 | pear\.registry\db.reg 23 | pear\.registry\db_dataobject.reg 24 | pear\.registry\event_dispatcher.reg 25 | pear\.registry\file.reg 26 | pear\.registry\file_csv.reg 27 | pear\.registry\file_find.reg 28 | pear\.registry\file_util.reg 29 | pear\.registry\html_common.reg 30 | pear\.registry\html_form.reg 31 | pear\.registry\html_table.reg 32 | pear\.registry\http.reg 33 | pear\.registry\http_download.reg 34 | pear\.registry\http_header.reg 35 | pear\.registry\http_request.reg 36 | pear\.registry\http_request2.reg 37 | pear\.registry\image_barcode.reg 38 | pear\.registry\image_color.reg 39 | pear\.registry\log.reg 40 | pear\.registry\mail.reg 41 | pear\.registry\mail_mime.reg 42 | pear\.registry\mail_mimedecode.reg 43 | pear\.registry\math_basex.reg 44 | pear\.registry\math_finance.reg 45 | pear\.registry\math_numerical_rootfinding.reg 46 | pear\.registry\mdb2.reg 47 | pear\.registry\mime_type.reg 48 | pear\.registry\mp3_id.reg 49 | pear\.registry\net_dime.reg 50 | pear\.registry\net_smtp.reg 51 | pear\.registry\net_socket.reg 52 | pear\.registry\net_url.reg 53 | pear\.registry\net_url2.reg 54 | pear\.registry\numbers_roman.reg 55 | pear\.registry\pager.reg 56 | pear\.registry\pear.reg 57 | pear\.registry\php_compat.reg 58 | pear\.registry\php_compatinfo.reg 59 | pear\.registry\phpdocumentor.reg 60 | pear\.registry\phpunit.reg 61 | pear\.registry\phpunit2.reg 62 | pear\.registry\services_akismet.reg 63 | pear\.registry\structures_graph.reg 64 | pear\.registry\system_folders.reg 65 | pear\.registry\text_diff.reg 66 | pear\.registry\text_wiki.reg 67 | pear\.registry\translation2.reg 68 | pear\.registry\var_dump.reg 69 | pear\.registry\xml_beautifier.reg 70 | pear\.registry\xml_fo2pdf.reg 71 | pear\.registry\xml_htmlsax3.reg 72 | pear\.registry\xml_parser.reg 73 | pear\.registry\xml_rpc.reg 74 | pear\.registry\xml_rpc2.reg 75 | pear\.registry\xml_util.reg 76 | pear\pearcmd.php 77 | pear\peclcmd.php 78 | pear\PhpDocumentor\phpDocumentor\common.inc.php 79 | pear\PhpDocumentor\phpDocumentor\IntermediateParser.inc 80 | peardev.bat 81 | pecl.bat 82 | phing.bat 83 | php.ini 84 | php_old.ini 85 | phpcov 86 | phpcov.bat 87 | phpcs 88 | phpcs.bat 89 | phpdoc 90 | phpdoc.bat 91 | phptok 92 | phptok.bat 93 | phpuml 94 | phpunit 95 | phpunit.bat 96 | scripts\phpcs-svn-pre-commit 97 | tests\Structures_Graph\tests\helper.inc 98 | Text\Highlighter\generate.bat -------------------------------------------------------------------------------- /src/Support/Console.php: -------------------------------------------------------------------------------- 1 | 'Program is terminating...' 9 | ]; 10 | 11 | public static function setDefaultMessages($defaultMessages = []) 12 | { 13 | if (is_array($defaultMessages)) { 14 | foreach (self::$defaultMessages as $key => $value) { 15 | if (array_key_exists($key, $defaultMessages) && !empty($defaultMessages[$key])) { 16 | self::$defaultMessages[$key] = $defaultMessages[$key]; 17 | } 18 | } 19 | } 20 | } 21 | 22 | public static function line($message = null, $breakLine = true, $beginAtColumn = 0) 23 | { 24 | $spaceBefore = ($beginAtColumn > 0) ? str_repeat(' ', $beginAtColumn) : ''; 25 | 26 | echo $spaceBefore . $message; 27 | 28 | if ($breakLine) { 29 | echo PHP_EOL; 30 | } 31 | } 32 | 33 | public static function breakline($multiplier = 1) 34 | { 35 | self::line(str_repeat(PHP_EOL, $multiplier), false); 36 | } 37 | 38 | public static function hrline($width = 83, $symbol = '-') 39 | { 40 | self::line(str_repeat($symbol, $width)); 41 | } 42 | 43 | public static function terminate($message = null, $exitStatus = 0, $silentMode = false) 44 | { 45 | if (! $silentMode) { 46 | if (! is_null($message)) { 47 | $message .= PHP_EOL; 48 | } 49 | 50 | self::line($message . self::$defaultMessages['terminate']); 51 | } 52 | 53 | exit($exitStatus); 54 | } 55 | 56 | public static function ask($message, $defaultValue = null) 57 | { 58 | self::line($message . ((! is_null($defaultValue)) ? ' ["' . $defaultValue . '"]' : null) . ': ', false); 59 | 60 | $answer = self::getInputFromKeyboard(((! is_null($defaultValue)) ? $defaultValue : null)); 61 | 62 | return $answer; 63 | } 64 | 65 | public static function confirm($question, $default = true, $trueChar = 'y', $falseChar = 'n') 66 | { 67 | $yes = strtolower($trueChar); 68 | $no = strtolower($falseChar); 69 | 70 | if ($default) { 71 | $yes = '"' . $trueChar . '"'; 72 | } else { 73 | $no = '"' . $falseChar . '"'; 74 | } 75 | 76 | self::line($question . ' [' . $yes . '|' . $no . ']: ', false); 77 | 78 | $answer = self::getInputFromKeyboard((($default) ? $trueChar : $falseChar)); 79 | 80 | return strtolower($answer) == $trueChar; 81 | } 82 | 83 | private static function getInputFromKeyboard($defaultValue = null) 84 | { 85 | $userInput = $defaultValue; 86 | $hStdin = fopen('php://stdin', 'r'); 87 | $userInput = (trim(fgets($hStdin, 256))); 88 | $userInput = (is_null($userInput) || $userInput === '') ? $defaultValue : $userInput; 89 | 90 | fclose($hStdin); 91 | 92 | return $userInput; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/Templates/xampp_config/httpd-xampp-php5.conf.tpl: -------------------------------------------------------------------------------- 1 | # 2 | # XAMPP settings 3 | # 4 | 5 | 6 | SetEnv MIBDIRS "{{xamppDir}}/php/extras/mibs" 7 | SetEnv MYSQL_HOME "\\xampp\\mysql\\bin" 8 | SetEnv OPENSSL_CONF "{{xamppDir}}/apache/bin/openssl.cnf" 9 | SetEnv PHP_PEAR_SYSCONF_DIR "\\xampp\\php" 10 | SetEnv PHPRC "\\xampp\\php" 11 | SetEnv TMP "\\xampp\\tmp" 12 | 13 | 14 | # 15 | # PHP-Module setup 16 | # 17 | LoadFile "{{xamppDir}}/php/php5ts.dll" 18 | LoadFile "{{xamppDir}}/php/libpq.dll" 19 | LoadModule php5_module "{{xamppDir}}/php/php5apache2_4.dll" 20 | 21 | 22 | SetHandler application/x-httpd-php 23 | 24 | 25 | SetHandler application/x-httpd-php-source 26 | 27 | 28 | # 29 | # PHP-CGI setup 30 | # 31 | # 32 | # SetHandler application/x-httpd-php-cgi 33 | # 34 | # 35 | # Action application/x-httpd-php-cgi "/php-cgi/php-cgi.exe" 36 | # 37 | 38 | 39 | 40 | PHPINIDir "{{xamppDir}}/php" 41 | 42 | 43 | 44 | AddType text/html .php .phps 45 | 46 | 47 | ScriptAlias /php-cgi/ "{{xamppDir}}/php/" 48 | 49 | AllowOverride None 50 | Options None 51 | Require all denied 52 | 53 | Require all granted 54 | 55 | 56 | 57 | 58 | 59 | SetHandler cgi-script 60 | 61 | 62 | SetHandler None 63 | 64 | 65 | 66 | 67 | 68 | 69 | php_admin_flag safe_mode off 70 | 71 | 72 | AllowOverride AuthConfig 73 | 74 | 75 | 76 | Alias /licenses "{{xamppDir}}/licenses/" 77 | 78 | Options +Indexes 79 | 80 | DirectoryIndexTextColor "#000000" 81 | DirectoryIndexBGColor "#f8e8a0" 82 | DirectoryIndexLinkColor "#bb3902" 83 | DirectoryIndexVLinkColor "#bb3902" 84 | DirectoryIndexALinkColor "#bb3902" 85 | 86 | Require local 87 | ErrorDocument 403 /error/XAMPP_FORBIDDEN.html.var 88 | 89 | 90 | Alias /phpmyadmin "{{xamppDir}}/phpMyAdmin/" 91 | 92 | AllowOverride AuthConfig 93 | Require local 94 | ErrorDocument 403 /error/XAMPP_FORBIDDEN.html.var 95 | 96 | 97 | Alias /webalizer "{{xamppDir}}/webalizer/" 98 | 99 | 100 | 101 | php_admin_flag safe_mode off 102 | 103 | 104 | AllowOverride AuthConfig 105 | Require local 106 | ErrorDocument 403 /error/XAMPP_FORBIDDEN.html.var 107 | 108 | 109 | -------------------------------------------------------------------------------- /src/Templates/xampp_config/httpd-xampp-php7.conf.tpl: -------------------------------------------------------------------------------- 1 | # 2 | # XAMPP settings 3 | # 4 | 5 | 6 | SetEnv MIBDIRS "{{xamppDir}}/php/extras/mibs" 7 | SetEnv MYSQL_HOME "\\xampp\\mysql\\bin" 8 | SetEnv OPENSSL_CONF "{{xamppDir}}/apache/bin/openssl.cnf" 9 | SetEnv PHP_PEAR_SYSCONF_DIR "\\xampp\\php" 10 | SetEnv PHPRC "\\xampp\\php" 11 | SetEnv TMP "\\xampp\\tmp" 12 | 13 | 14 | # 15 | # PHP-Module setup 16 | # 17 | LoadFile "{{xamppDir}}/php/php7ts.dll" 18 | LoadFile "{{xamppDir}}/php/libpq.dll" 19 | LoadModule php7_module "{{xamppDir}}/php/php7apache2_4.dll" 20 | 21 | 22 | SetHandler application/x-httpd-php 23 | 24 | 25 | SetHandler application/x-httpd-php-source 26 | 27 | 28 | # 29 | # PHP-CGI setup 30 | # 31 | # 32 | # SetHandler application/x-httpd-php-cgi 33 | # 34 | # 35 | # Action application/x-httpd-php-cgi "/php-cgi/php-cgi.exe" 36 | # 37 | 38 | 39 | 40 | PHPINIDir "{{xamppDir}}/php" 41 | 42 | 43 | 44 | AddType text/html .php .phps 45 | 46 | 47 | ScriptAlias /php-cgi/ "{{xamppDir}}/php/" 48 | 49 | AllowOverride None 50 | Options None 51 | Require all denied 52 | 53 | Require all granted 54 | 55 | 56 | 57 | 58 | 59 | SetHandler cgi-script 60 | 61 | 62 | SetHandler None 63 | 64 | 65 | 66 | 67 | 68 | 69 | php_admin_flag safe_mode off 70 | 71 | 72 | AllowOverride AuthConfig 73 | 74 | 75 | 76 | Alias /licenses "{{xamppDir}}/licenses/" 77 | 78 | Options +Indexes 79 | 80 | DirectoryIndexTextColor "#000000" 81 | DirectoryIndexBGColor "#f8e8a0" 82 | DirectoryIndexLinkColor "#bb3902" 83 | DirectoryIndexVLinkColor "#bb3902" 84 | DirectoryIndexALinkColor "#bb3902" 85 | 86 | Require local 87 | ErrorDocument 403 /error/XAMPP_FORBIDDEN.html.var 88 | 89 | 90 | Alias /phpmyadmin "{{xamppDir}}/phpMyAdmin/" 91 | 92 | AllowOverride AuthConfig 93 | Require local 94 | ErrorDocument 403 /error/XAMPP_FORBIDDEN.html.var 95 | 96 | 97 | Alias /webalizer "{{xamppDir}}/webalizer/" 98 | 99 | 100 | 101 | php_admin_flag safe_mode off 102 | 103 | 104 | AllowOverride AuthConfig 105 | Require local 106 | ErrorDocument 403 /error/XAMPP_FORBIDDEN.html.var 107 | 108 | 109 | -------------------------------------------------------------------------------- /src/Templates/xampp_config/httpd-xampp-php8.conf.tpl: -------------------------------------------------------------------------------- 1 | # 2 | # XAMPP settings 3 | # 4 | 5 | 6 | SetEnv MIBDIRS "{{xamppDir}}/php/extras/mibs" 7 | SetEnv MYSQL_HOME "\\xampp\\mysql\\bin" 8 | SetEnv OPENSSL_CONF "{{xamppDir}}/apache/bin/openssl.cnf" 9 | SetEnv PHP_PEAR_SYSCONF_DIR "\\xampp\\php" 10 | SetEnv PHPRC "\\xampp\\php" 11 | SetEnv TMP "\\xampp\\tmp" 12 | 13 | 14 | # 15 | # PHP-Module setup 16 | # 17 | LoadFile "{{xamppDir}}/php/php8ts.dll" 18 | LoadFile "{{xamppDir}}/php/libpq.dll" 19 | LoadFile "{{xamppDir}}/php/libsqlite3.dll" 20 | LoadModule php_module "{{xamppDir}}/php/php8apache2_4.dll" 21 | 22 | 23 | SetHandler application/x-httpd-php 24 | 25 | 26 | SetHandler application/x-httpd-php-source 27 | 28 | 29 | # 30 | # PHP-CGI setup 31 | # 32 | # 33 | # SetHandler application/x-httpd-php-cgi 34 | # 35 | # 36 | # Action application/x-httpd-php-cgi "/php-cgi/php-cgi.exe" 37 | # 38 | 39 | 40 | 41 | PHPINIDir "{{xamppDir}}/php" 42 | 43 | 44 | 45 | AddType text/html .php .phps 46 | 47 | 48 | ScriptAlias /php-cgi/ "{{xamppDir}}/php/" 49 | 50 | AllowOverride None 51 | Options None 52 | Require all denied 53 | 54 | Require all granted 55 | 56 | 57 | 58 | 59 | 60 | SetHandler cgi-script 61 | 62 | 63 | SetHandler None 64 | 65 | 66 | 67 | 68 | 69 | 70 | php_admin_flag safe_mode off 71 | 72 | 73 | AllowOverride AuthConfig 74 | 75 | 76 | 77 | Alias /licenses "{{xamppDir}}/licenses/" 78 | 79 | Options +Indexes 80 | 81 | DirectoryIndexTextColor "#000000" 82 | DirectoryIndexBGColor "#f8e8a0" 83 | DirectoryIndexLinkColor "#bb3902" 84 | DirectoryIndexVLinkColor "#bb3902" 85 | DirectoryIndexALinkColor "#bb3902" 86 | 87 | Require local 88 | ErrorDocument 403 /error/XAMPP_FORBIDDEN.html.var 89 | 90 | 91 | Alias /phpmyadmin "{{xamppDir}}/phpMyAdmin/" 92 | 93 | AllowOverride AuthConfig 94 | Require local 95 | ErrorDocument 403 /error/XAMPP_FORBIDDEN.html.var 96 | 97 | 98 | Alias /webalizer "{{xamppDir}}/webalizer/" 99 | 100 | 101 | 102 | php_admin_flag safe_mode off 103 | 104 | 105 | AllowOverride AuthConfig 106 | Require local 107 | ErrorDocument 403 /error/XAMPP_FORBIDDEN.html.var 108 | 109 | 110 | -------------------------------------------------------------------------------- /xphp.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | title Xampp PHP Switcher 3 | setlocal EnableExtensions EnableDelayedExpansion 4 | 5 | cd /D %~dp0 6 | 7 | rem --------------------------------------------- 8 | for /F "tokens=* USEBACKQ" %%v in (`where php`) do ( 9 | if not exist "%%v" goto phpBinNotFound 10 | ) 11 | 12 | set XPHP_APP_DIR=%~dp0 13 | if not "%XPHP_APP_DIR:~-2%"==":\" set XPHP_APP_DIR=%XPHP_APP_DIR:~0,-1% 14 | 15 | set XPHP_SRC_DIR=%XPHP_APP_DIR%\src 16 | set XPHP_TMP_DIR=%XPHP_APP_DIR%\tmp 17 | set XPHP_PHP_CONTROLLER=%XPHP_SRC_DIR%\xphp.php 18 | set XPHP_POWER_EXECUTOR=%XPHP_SRC_DIR%\power_exec.vbs 19 | 20 | if not exist "%XPHP_TMP_DIR%" mkdir "%XPHP_TMP_DIR%" 21 | goto startCommand 22 | 23 | rem --------------------------------------------- 24 | :phpBinNotFound 25 | echo. 26 | echo Cannot find PHP CLI. 27 | echo Make sure you have add the path to your PHP directory into Windows Path Environment Variable. 28 | call %~fx0:clearEnvVars 29 | exit /B 1 30 | 31 | rem --------------------------------------------- 32 | :installed 33 | echo. 34 | echo Xampp PHP Switcher is already integrated into Xampp. 35 | echo No need to do it again. 36 | echo. 37 | pause>nul|set/p =Press any key to exit terminal... 38 | call :clearEnvVars 39 | exit /B 40 | 41 | rem --------------------------------------------- 42 | :installationFailed 43 | echo. 44 | echo Installation Xampp PHP Switcher failed. 45 | echo Please review the instructions carefully before installation. 46 | if exist "%XPHP_APP_DIR%\settings.ini" del /Q "%XPHP_APP_DIR%\settings.ini" 47 | echo. 48 | pause>nul|set/p =Press any key to exit terminal... 49 | call :clearEnvVars 50 | exit 1 51 | 52 | rem --------------------------------------------- 53 | :missingArgs 54 | echo. 55 | echo Xampp PHP Switcher error: The "command" argument is missing. 56 | echo. 57 | goto help 58 | call :clearEnvVars 59 | exit /B 1 60 | 61 | rem --------------------------------------------- 62 | :clearEnvVars 63 | set XPHP_APP_DIR= 64 | set XPHP_TMP_DIR= 65 | set XPHP_POWER_EXECUTOR= 66 | set XPHP_PHP_CONTROLLER= 67 | exit /B 68 | 69 | rem --------------------------------------------- 70 | :help 71 | type %XPHP_APP_DIR%\xphp.hlp 72 | call :clearEnvVars 73 | exit /B 74 | 75 | rem --------------------------------------------- 76 | :install 77 | if exist "%XPHP_APP_DIR%\settings.ini" goto installed 78 | FSUTIL dirty query %SystemDrive%>nul 79 | if %errorLevel% NEQ 0 ( 80 | echo. 81 | echo This process can only be run with elevated permission. 82 | pause>nul|set/p =Press any key to start this process in Administrator mode... 83 | echo. 84 | cscript //NoLogo "%XPHP_POWER_EXECUTOR%" -e -x -n "%~fx0" "install" 85 | if errorLevel 1 ( 86 | echo The installation was canceled by user. 87 | exit /B 1 88 | ) else ( 89 | echo The installation started in new window with Elevated permission. 90 | exit /B 91 | ) 92 | ) 93 | 94 | php -n -d output_buffering=0 "%XPHP_PHP_CONTROLLER%" "install" 95 | 96 | if %errorLevel% EQU 1 goto installationFailed 97 | if %errorLevel% EQU 2 ( 98 | echo. 99 | pause>nul|set/p =Press any key to exit terminal... 100 | call :clearEnvVars 101 | exit /B 2 102 | ) 103 | 104 | echo|set/p =Moving directory of the current PHP build into the repository... 105 | for /F "tokens=* USEBACKQ" %%v in ("%XPHP_TMP_DIR%\.phpdir") do (set XPHP_PHP_DIR=%%v) 106 | for /F "tokens=* USEBACKQ" %%v in ("%XPHP_TMP_DIR%\.storage_path") do (set XPHP_PHP_REPO=%%v) 107 | move /Y "%XPHP_PHP_DIR%" "%XPHP_PHP_REPO%" >nul 2>&1 108 | echo Successful 109 | 110 | echo|set/p =Creating symbolic link to the current PHP build in repository... 111 | mklink /J "%XPHP_PHP_DIR%" "%XPHP_PHP_REPO%" >nul 2>&1 112 | echo Successful 113 | 114 | del /Q "%XPHP_TMP_DIR%\." 115 | set XPHP_PHP_DIR= 116 | set XPHP_PHP_REPO= 117 | 118 | echo. 119 | echo ----------------------------------------------------------------------------------- 120 | echo XAMPP PHP SWITCHER WAS INSTALLED SUCCESSFULLY. 121 | echo TO START USING IT, PLEASE EXIT YOUR TERMINAL TO 122 | echo DELETE TEMPORARY PROCESS ENVIRONMENT VARIABLES. 123 | echo. 124 | pause>nul|set/p =Press any key to exit terminal... 125 | call :clearEnvVars 126 | exit 127 | 128 | rem --------------------------------------------- 129 | :addVersion 130 | php -n -d output_buffering=0 %XPHP_PHP_CONTROLLER% "addVersion" "%~2" 131 | call :clearEnvVars 132 | exit /B %errorLevel% 133 | 134 | rem --------------------------------------------- 135 | :removeVersion 136 | php -n -d output_buffering=0 %XPHP_PHP_CONTROLLER% "removeVersion" "%~2" 137 | call :clearEnvVars 138 | exit /B %errorLevel% 139 | 140 | rem --------------------------------------------- 141 | :listVersions 142 | php -n -d output_buffering=0 %XPHP_PHP_CONTROLLER% "listVersions" 143 | call :clearEnvVars 144 | exit /B %errorLevel% 145 | 146 | rem --------------------------------------------- 147 | :showVersion 148 | php -n -d output_buffering=0 %XPHP_PHP_CONTROLLER% "showVersion" "%~2" 149 | call :clearEnvVars 150 | exit /B %errorLevel% 151 | 152 | rem --------------------------------------------- 153 | :switchVersion 154 | php -n -d output_buffering=0 %XPHP_PHP_CONTROLLER% "switchVersion" "%~2" 155 | call :clearEnvVars 156 | exit /B %errorLevel% 157 | 158 | rem --------------------------------------------- 159 | :startCommand 160 | cls 161 | if "%~1"=="" goto missingArgs 162 | if "%~1"=="help" goto help 163 | if "%~1"=="install" goto install 164 | if "%~1"=="add" goto addVersion 165 | if "%~1"=="remove" goto removeVersion 166 | if "%~1"=="list" goto listVersions 167 | if "%~1"=="info" goto showVersion 168 | if "%~1"=="switch" goto switchVersion 169 | 170 | rem Call command with unknown param ------------- 171 | echo. 172 | echo Xampp PHP Switcher error: "%~1" is invalid xphp command. 173 | echo. 174 | goto help 175 | 176 | endlocal -------------------------------------------------------------------------------- /src/Support/Application.php: -------------------------------------------------------------------------------- 1 | 'Xampp PHP Switcher is terminating...']); 14 | 15 | $this->setting = new Setting; 16 | 17 | $this->defineAppPaths(); 18 | 19 | if ($initAdditional) { 20 | $this->initAdditional(); 21 | } 22 | } 23 | 24 | protected function stopApache($askConfirm = true, $question = null) 25 | { 26 | if ($askConfirm) { 27 | $question = $question ?: 'Do you want to stop Apache?'; 28 | $confirm = Console::confirm($question); 29 | } else { 30 | $confirm = true; 31 | } 32 | 33 | if ($confirm) { 34 | $message = 'Stopping Apache Httpd...'; 35 | Console::line($message, false); 36 | self::powerExec('"' . $this->paths['xamppDir'] . '\apache_stop.bat"', '-w -i -n'); 37 | Console::line('Successful', true, max(73 - strlen($message), 1)); 38 | } 39 | } 40 | 41 | protected function startApache($askConfirm = true, $question = null) 42 | { 43 | if (self::isApacheRunning()) { 44 | self::restartApache($askConfirm, ($question ? str_replace('start', 'restart', $question) : null)); 45 | } else { 46 | if ($askConfirm) { 47 | $question = $question ?: 'Do you want to start Apache?'; 48 | $confirm = Console::confirm($question); 49 | } else { 50 | $confirm = true; 51 | } 52 | 53 | if ($confirm) { 54 | $message = 'Starting Apache Httpd...'; 55 | Console::line($message, false); 56 | self::powerExec('"' . $this->paths['xamppDir'] . '\apache_start.bat"', '-i -n'); 57 | Console::line('Successful', true, max(73 - strlen($message), 1)); 58 | } 59 | } 60 | } 61 | 62 | protected function restartApache($askConfirm = true, $question = null) 63 | { 64 | if ($askConfirm) { 65 | $question = $question ?: 'Do you want to restart Apache?'; 66 | $confirm = Console::confirm($question); 67 | } else { 68 | $confirm = true; 69 | } 70 | 71 | if ($confirm) { 72 | Console::breakline(); 73 | 74 | self::stopApache(false); 75 | self::startApache(false); 76 | } 77 | } 78 | 79 | protected function registerPath($askConfirm = true, $question = null) 80 | { 81 | if ($askConfirm) { 82 | $question = $question ?: 'Do you want to change the path of this app to "' . $this->paths['appDir'] . '"?'; 83 | $confirm = Console::confirm($question); 84 | } else { 85 | $confirm = true; 86 | } 87 | 88 | if ($confirm) { 89 | $message = 'Registering application\'s path into Windows Path Environment...'; 90 | Console::line($message, false); 91 | 92 | self::powerExec('cscript "' . $this->paths['pathRegister'] . '" "' .$this->paths['appDir']. '"', '-w -i -e -n', $outputVal, $exitCode); 93 | 94 | if ($exitCode == 0) { 95 | Console::line('Successful', true, max(73 - strlen($message), 1)); 96 | return true; 97 | } 98 | 99 | Console::line('Failed', true, max(77 - strlen($message), 1)); 100 | return false; 101 | } 102 | } 103 | 104 | protected function initAdditional() 105 | { 106 | if (!isset($this->paths['xamppDir']) || !isset($this->paths['apacheDir']) || !isset($this->paths['phpDir'])) { 107 | $this->detectXamppPaths(); 108 | } 109 | 110 | $this->paths['httpdXampp'] = $this->paths['apacheDir'] . '\conf\extra\httpd-xampp.conf'; 111 | $this->paths['httpdXamppPHP'] = $this->paths['apacheDir'] . '\conf\extra\httpd-xampp-php{{php_major_version}}.conf'; 112 | 113 | $this->repository = new VersionRepository(get_architecture_phpdir($this->paths['phpDir']), $this->paths['xamppDir'] . '\phpRepository', false); 114 | 115 | return $this; 116 | } 117 | 118 | protected function powerExec($command, $arguments = null, &$outputArray = null, &$statusCode = null) 119 | { 120 | if (is_file($this->paths['powerExecutor'])) { 121 | if (is_array($arguments)) { 122 | $arguments = '"' . trim(implode('" "', $arguments)) . '"'; 123 | } elseif (is_string($arguments)) { 124 | $arguments = trim($arguments); 125 | } else { 126 | $arguments = trim(strval($arguments)); 127 | } 128 | 129 | $outputArray = $statusCode = null; 130 | 131 | return exec('cscript //NoLogo "' . $this->paths['powerExecutor'] . '" ' . $arguments . ' ' . $command, $outputArray, $statusCode); 132 | } 133 | 134 | $message = 'Cannot find the "' . $this->paths['powerExecutor'] . '" implementer.'; 135 | $outputArray = [$message]; 136 | $statusCode = 1; 137 | 138 | return $message; 139 | } 140 | 141 | protected function isApacheRunning() 142 | { 143 | $lastRow = exec('tasklist /NH /FI "IMAGENAME eq httpd.exe" 2>nul', $output, $status); 144 | 145 | if ($lastRow == 'INFO: No tasks are running which match the specified criteria.') { 146 | return false; 147 | } 148 | 149 | return true; 150 | } 151 | 152 | private function defineAppPaths() 153 | { 154 | $appDir = realpath(getenv('XPHP_APP_DIR')); 155 | $srcDir = $appDir . '\src'; 156 | $tmpDir = getenv('XPHP_TMP_DIR'); 157 | 158 | $this->paths['appDir'] = $appDir; 159 | $this->paths['srcDir'] = $srcDir; 160 | $this->paths['httpdXamppTemplate'] = $srcDir . '\Templates\xampp_config\httpd-xampp-php{{php_major_version}}.conf.tpl'; 161 | $this->paths['pathRegister'] = $srcDir . '\path_register.vbs'; 162 | $this->paths['powerExecutor'] = $srcDir . '\power_exec.vbs'; 163 | $this->paths['tmpDir'] = $tmpDir; 164 | 165 | if (! is_dir($tmpDir)) { 166 | @mkdir($tmpDir, 0755, true); 167 | } 168 | } 169 | 170 | private function detectXamppPaths() 171 | { 172 | // Force reload settings 173 | $this->setting->reloadSettings(); 174 | 175 | $xamppDir = realpath($this->setting->get('DirectoryPaths', 'Xampp')); 176 | $apacheDir = $this->setting->get('DirectoryPaths', 'Apache'); 177 | $phpDir = $this->setting->get('DirectoryPaths', 'Php'); 178 | 179 | // define Xampp directory path 180 | if (! $xamppDir || ! is_file($xamppDir . '\xampp-control.exe')) { 181 | Console::breakline(); 182 | 183 | $message = 'Cannot find Xampp directory.' . PHP_EOL; 184 | $message .= 'Please check the configuration path to the Xampp directory in file "' . $this->paths['appDir'] . '\settings.ini".'; 185 | 186 | Console::terminate($message, 1); 187 | } 188 | 189 | $this->paths['xamppDir'] = $xamppDir; 190 | 191 | // define Apache directory path 192 | if (! $apacheDir) { 193 | $apacheDir = $xamppDir . '\apache'; 194 | } 195 | 196 | if (! is_file($apacheDir . '\bin\httpd.exe')) { 197 | Console::breakline(); 198 | 199 | $message = 'Cannot find Apache directory.' . PHP_EOL; 200 | $message .= 'Please check the configuration path to the Apache directory in file "' . $this->paths['appDir'] . '\settings.ini".'; 201 | 202 | Console::terminate($message, 1); 203 | } 204 | 205 | $this->paths['apacheDir'] = $apacheDir; 206 | 207 | // define PHP directory path 208 | if (! $phpDir) { 209 | $phpDir = $xamppDir . '\php'; 210 | } 211 | 212 | if (! is_file($phpDir . '\php.exe')) { 213 | Console::breakline(); 214 | 215 | $message = 'Cannot find PHP directory.' . PHP_EOL; 216 | $message .= 'Please check the configuration path to the PHP directory in file "' . $this->paths['appDir'] . '\settings.ini".'; 217 | 218 | Console::terminate($message, 1); 219 | } 220 | 221 | $this->paths['phpDir'] = $phpDir; 222 | 223 | return $this; 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/Support/Installer.php: -------------------------------------------------------------------------------- 1 | paths['appDir'] . '\settings.ini')) { 12 | Console::breakline(); 13 | Console::line('Xampp PHP Switcher is already integrated into Xampp.'); 14 | Console::line('No need to do it again.'); 15 | Console::terminate(null, 2, true); 16 | } 17 | } 18 | 19 | public function install() 20 | { 21 | Console::line('Welcome to Xampp PHP Switcher installer.'); 22 | Console::line('This installer will perform following tasks:'); 23 | Console::breakline(); 24 | 25 | Console::line('1. Register path into Windows Path Environment Variable, so you can call this app anywhere later.'); 26 | Console::line('2. Backup the current "httpd-xampp.conf" file of your Xampp application.'); 27 | Console::line('3. Improved the current "httpd-xampp.conf" file for switching between PHP versions.'); 28 | Console::line('4. Move directory of the current PHP build into the repository and create symbolic link to it.'); 29 | Console::breakline(); 30 | 31 | $continue = Console::confirm('Do you agree to continue?'); 32 | 33 | if (! $continue) { 34 | Console::breakline(); 35 | Console::line('The installation was canceled by user.'); 36 | Console::terminate(null, 2, true); 37 | } 38 | 39 | Console::breakline(); 40 | 41 | $this->askInstallConfig(); 42 | 43 | Console::hrline(); 44 | Console::line('Start intergrating Xampp PHP Switcher into your Xampp.'); 45 | Console::breakline(); 46 | 47 | $this->registerPath(); 48 | $this->editHttpdXampp(); 49 | } 50 | 51 | private function askInstallConfig() 52 | { 53 | $phpDir = dirname(PHP_BINARY); 54 | 55 | Console::line('First, provide the path to your Xampp directory for Xampp PHP Switcher.'); 56 | 57 | if (is_file($phpDir . '\..\xampp-control.exe')) { 58 | $detectedXamppDir = realpath($phpDir . '\..\\'); 59 | 60 | Console::line('Xampp PHP Switcher has detected that directory "' . $detectedXamppDir . '" could be your Xampp directory.'); 61 | Console::breakline(); 62 | 63 | $confirmXamppDir = Console::confirm('Is that the actual path to your Xampp directory?'); 64 | 65 | Console::breakline(); 66 | } 67 | 68 | $xamppDir = (isset($detectedXamppDir) && $confirmXamppDir) ? $detectedXamppDir : $this->tryGetXamppDir(); 69 | 70 | $this->setting->set('DirectoryPaths', 'Xampp', $xamppDir); 71 | 72 | $this->paths['xamppDir'] = $xamppDir; 73 | 74 | if (is_file($xamppDir . '\apache\bin\httpd.exe')) { 75 | $this->paths['apacheDir'] = $xamppDir . '\apache'; 76 | } else { 77 | Console::line('Next, because Xampp PHP Switcher does not detect the path to the Apache directory, you need to provide it manually.'); 78 | Console::breakline(); 79 | 80 | $apacheDir = $this->tryGetApacheDir(); 81 | 82 | $this->setting->set('DirectoryPaths', 'Apache', $apacheDir); 83 | 84 | $this->paths['apacheDir'] = $apacheDir; 85 | } 86 | 87 | if (is_file($xamppDir . '\php\php.exe')) { 88 | $this->paths['phpDir'] = $xamppDir . '\php'; 89 | } else { 90 | Console::line('Next, because Xampp PHP Switcher does not detect the path to the PHP directory, you need to provide it manually.'); 91 | Console::breakline(); 92 | 93 | $phpDir = $this->tryGetPHPDir(); 94 | 95 | $this->setting->set('DirectoryPaths', 'PHP', $phpDir); 96 | 97 | $this->paths['phpDir'] = $phpDir; 98 | } 99 | 100 | if (! $this->setting->save()) { 101 | Console::breakline(); 102 | Console::line('Installation settings cannot be saved.'); 103 | Console::terminate('Cancel the installation.', 1); 104 | } 105 | 106 | $this->setting->reloadSettings(); 107 | $this->initAdditional(); 108 | } 109 | 110 | protected function registerPath($askConfirm = true, $question = null) 111 | { 112 | $result = parent::registerPath(false); 113 | 114 | if (! $result) { 115 | Console::breakline(); 116 | Console::line('Don\'t worry. This does not affect the installation process.'); 117 | Console::line('You can register the path manually after installation.'); 118 | Console::line('See [https://helpdeskgeek.com/windows-10/add-windows-path-environment-variable/] to know how to add Windows Path Environment Variable.'); 119 | } 120 | } 121 | 122 | private function editHttpdXampp() 123 | { 124 | // Detecting informations of current PHP 125 | $message = 'Detecting informations of current PHP build...'; 126 | 127 | Console::line($message, false); 128 | 129 | $version = get_version_phpdir($this->paths['phpDir']); 130 | $majorVersion = get_major_phpversion($version); 131 | $storageConfig = [ 132 | 'BuildInfo' => [ 133 | 'Version' => $version, 134 | 'Architecture' => get_architecture_phpdir($this->paths['phpDir']), 135 | 'Compiler' => get_compiler_phpdir($this->paths['phpDir']), 136 | 'BuildDate' => get_builddate_phpdir($this->paths['phpDir']), 137 | 'ZendVersion' => get_zendversion_phpdir($this->paths['phpDir']), 138 | ], 139 | 'RepoImporting' => [ 140 | 'PathStandardized' => $this->paths['xamppDir'] 141 | ] 142 | ]; 143 | 144 | $this->repository->saveStorageConfig($this->paths['phpDir'], $storageConfig); 145 | Console::line('Successful', true, max(73 - strlen($message), 1)); 146 | 147 | $httpdXampp = $this->paths['httpdXampp']; 148 | $httpdXamppPHP = str_replace('{{php_major_version}}', $majorVersion, $this->paths['httpdXamppPHP']); 149 | $backupFile = dirname($httpdXampp) . '\backup\httpd-xampp.conf'; 150 | 151 | if (is_file($httpdXamppPHP)) { 152 | $originContent = @file_get_contents($httpdXamppPHP); 153 | $httpdXamppCloned = true; 154 | } else { 155 | $originContent = @file_get_contents($httpdXampp); 156 | $httpdXamppCloned = false; 157 | } 158 | 159 | // Backup the existing "httpd-xampp.conf" file 160 | $message = 'Backing up the current "httpd-xampp.conf" file...'; 161 | 162 | Console::line($message, false); 163 | 164 | if (! is_file($backupFile)) { 165 | $backupDir = dirname($backupFile); 166 | 167 | if (! is_dir($backupDir)) { 168 | mkdir($backupDir, 0755, true); 169 | } 170 | 171 | @file_put_contents($backupFile, $originContent); 172 | } 173 | 174 | Console::line('Successful', true, max(73 - strlen($message), 1)); 175 | 176 | // Improving the existing "httpd-xampp.conf" file 177 | $message = 'Improving the current "httpd-xampp.conf" file...'; 178 | 179 | Console::line($message, false); 180 | 181 | if (! $httpdXamppCloned) { 182 | @file_put_contents($httpdXamppPHP, $originContent); 183 | } 184 | 185 | $newContent = 'Include "' . relative_path($this->paths['apacheDir'], $httpdXamppPHP, '/') . '"' . PHP_EOL; 186 | 187 | @file_put_contents($httpdXampp, $newContent); 188 | Console::line('Successful', true, max(73 - strlen($message), 1)); 189 | 190 | // Create temporary files to continue config in batch file 191 | @file_put_contents($this->paths['tmpDir'] . '\.phpdir', $this->paths['phpDir']); 192 | @file_put_contents($this->paths['tmpDir'] . '\.storage_path', $this->repository->buildStoragePath($version)); 193 | } 194 | 195 | private function tryGetXamppDir() 196 | { 197 | $xamppDir = ''; 198 | $repeat = 0; 199 | 200 | while (! is_file(rtrim($xamppDir, '\\/') . '\xampp-control.exe')) { 201 | if ($repeat == 4) { 202 | Console::line('You have not provided correct information multiple times.'); 203 | Console::terminate('Cancel the installation.', 1); 204 | } 205 | 206 | if ($repeat == 0) { 207 | $xamppDir = Console::ask('Enter the path to your Xampp directory'); 208 | } else { 209 | Console::line('The path provided is not the path to the actual Xampp directory.'); 210 | 211 | $xamppDir = Console::ask('Please provide it again'); 212 | } 213 | 214 | Console::breakline(); 215 | 216 | $xamppDir = str_replace('/', DS, $xamppDir); 217 | 218 | $repeat++; 219 | } 220 | 221 | return $xamppDir; 222 | } 223 | 224 | private function tryGetApacheDir() 225 | { 226 | $apacheDir = ''; 227 | $repeat = 0; 228 | 229 | while (! is_file(rtrim($apacheDir, '\\/') . '\bin\httpd.exe')) { 230 | if ($repeat == 4) { 231 | Console::line('You have not provided correct information multiple times.'); 232 | Console::terminate('Cancel the installation.', 1); 233 | } 234 | 235 | if ($repeat == 0) { 236 | $apacheDir = Console::ask('Enter the path to your Apache directory'); 237 | } else { 238 | Console::line('The path provided is not the path to the actual Apache directory.'); 239 | 240 | $apacheDir = Console::ask('Please provide it again'); 241 | } 242 | 243 | Console::breakline(); 244 | 245 | $apacheDir = str_replace('/', DS, $apacheDir); 246 | 247 | $repeat++; 248 | } 249 | 250 | return $apacheDir; 251 | } 252 | 253 | private function tryGetPHPDir() 254 | { 255 | $phpDir = ''; 256 | $repeat = 0; 257 | 258 | while (! is_file(rtrim($phpDir, '\\/') . '\php.exe')) { 259 | if ($repeat == 4) { 260 | Console::line('You have not provided correct information multiple times.'); 261 | Console::terminate('Cancel the installation.', 1); 262 | } 263 | 264 | if ($repeat == 0) { 265 | $phpDir = Console::ask('Enter the path to your PHP directory'); 266 | } else { 267 | Console::line('The path provided is not the path to the actual PHP directory.'); 268 | 269 | $phpDir = Console::ask('Please provide it again'); 270 | } 271 | 272 | Console::breakline(); 273 | 274 | $phpDir = str_replace('/', DS, $phpDir); 275 | 276 | $repeat++; 277 | } 278 | 279 | return $phpDir; 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Xampp PHP Switcher 2 | Use and switch between different versions of PHP for Xampp on Windows OS. 3 | 4 | ![Xampp-PHP-Switcher](https://user-images.githubusercontent.com/9862115/72956719-5b74da80-3dd3-11ea-8ce8-e311b275b434.jpg) 5 | 6 | Xampp is an easy to install Apache distribution containing MariaDB, PHP, and Perl. It has been around for more than 10 years – there is a huge community behind it. Currently, there are many other applications launched with more advantages. However, many people still love Xampp because of its ease of use. The problem is that quite a lot of people have not exploited the full power of Xampp. 7 | 8 | Therefore, I have created 2 separate projects to take full advantage of what Xampp has to add to the features that many people desire to have in Xampp: 9 | 10 | * One is the feature that allows the use of different versions of PHP (this project). 11 | * One is the vhosts management feature, comes with self-signed SSL certificates (see [here](https://github.com/JackieDo/Xampp-vHosts-Manager)). 12 | 13 | With this project, using and switching between different PHP versions for Xampp became easy. This will be useful for those who want to ensure their applications work smoothly on different PHP platforms or those who are researching PHP programming. 14 | 15 | > _Note: Currently this project only supports Windows users._ 16 | 17 | # Features of this project 18 | * Install and manage various PHP builds for a single Xampp version. 19 | * Switch between different versions of PHP quickly and easily. 20 | 21 | # Overview 22 | Look at one of the following topics to learn more about Xampp PHP Switcher. 23 | 24 | - [Xampp PHP Switcher](#xampp-php-switcher) 25 | - [Features of this project](#features-of-this-project) 26 | - [Overview](#overview) 27 | - [Compatibility](#compatibility) 28 | - [Requirement](#requirement) 29 | - [Installation](#installation) 30 | - [Via Composer Create-Project](#via-composer-create-project) 31 | - [Via Manual Download](#via-manual-download) 32 | - [Updation](#updation) 33 | - [Add other PHP versions to your Xampp](#add-other-php-versions-to-your-xampp) 34 | - [Important notes](#important-notes) 35 | - [Step by step process of adding build](#step-by-step-process-of-adding-build) 36 | - [Check architectural information of the current build](#check-architectural-information-of-the-current-build) 37 | - [Download compatible PHP builds](#download-compatible-php-builds) 38 | - [Add recent downloaded PHP build](#add-recent-downloaded-php-build) 39 | - [Behind the process of adding PHP build](#behind-the-process-of-adding-php-build) 40 | - [Update the process of adding PHP builds in the future](#update-the-process-of-adding-php-builds-in-the-future) 41 | - [Usage](#usage) 42 | - [Display the help message](#display-the-help-message) 43 | - [Display information of current PHP version](#display-information-of-current-php-version) 44 | - [Display information of existing PHP version in repository](#display-information-of-existing-php-version-in-repository) 45 | - [List all existing PHP versions in repository](#list-all-existing-php-versions-in-repository) 46 | - [Remove an existing PHP version outof repository](#remove-an-existing-php-version-outof-repository) 47 | - [Add one PHP version to repository](#add-one-php-version-to-repository) 48 | - [Switch to specific PHP version to use](#switch-to-specific-php-version-to-use) 49 | - [The file `php.ini` editing](#the-file-phpini-editing) 50 | - [License](#license) 51 | 52 | ## Compatibility 53 | 54 | * Compatible with all installed Xampp versions using PHP 5.4 or higher. 55 | * Currently, this project does not yet support the portable Xampp version. 56 | 57 | ## Requirement 58 | Xampp PHP Switcher takes full advantage of what's included in Xampp, nothing more needed. So, you just need following things: 59 | 60 | * Xampp installed successfully. 61 | * Added the path to PHP directory of Xampp into Windows Path Environment Variable. 62 | * (Optional) Installed Composer. 63 | 64 | > _Note: See [here](https://helpdeskgeek.com/windows-10/add-windows-path-environment-variable/) to know how to add Windows Path Environment Variable._ 65 | 66 | ## Installation 67 | There are two installation methods, via Composer or manual download. It is recommended to use the method via Composer if you already have it installed. 68 | 69 | #### Via Composer Create-Project 70 | * Open a terminal. 71 | * Navigate to the directory you want to install Xampp PHP Switcher into `(example: cd /D D:\)`. 72 | * Run composer create-project command: 73 | ``` 74 | $ composer create-project jackiedo/xampp-php-switcher xphp "1.*" 75 | ``` 76 | 77 | #### Via Manual Download 78 | * Download the [latest release version](https://github.com/JackieDo/Xampp-PHP-Switcher/releases/latest). 79 | * Extract the archive to a shared location `(example: D:\xphp)`. Note: Should not place in `C:\Program Files` or anywhere else that would require Administrator access for modifying configuration files. 80 | * Open a terminal in Administrator mode `(run as Administrator)`. 81 | * Navigate to the directory you have placed Xampp PHP Switcher `(example: cd /D D:\xphp)`. 82 | * Execute the command `xphp install` and follow the required steps. 83 | * Exit terminal (to remove temporary environment variables). 84 | 85 | > _Note: See [here](https://www.howtogeek.com/194041/how-to-open-the-command-prompt-as-administrator-in-windows-8.1/) to know how to to open the command prompt as Administrator._ 86 | 87 | ## Updation 88 | Currently, Xampp PHP Switcher does not have an automatic update mechanism, we have to do it manually step by step as follows: 89 | 90 | * Backup the file `settings.ini` in the folder where Xampp PHP Switcher application is installed to somewhere else. 91 | * Remove all files and sub-folders in Xampp PHP Switcher application folder. 92 | * Download the [latest release version](https://github.com/JackieDo/Xampp-PHP-Switcher/releases/latest) and extract it into Xampp PHP Switcher application folder. 93 | * Copy the previously backed up `settings.ini` file into the Xampp PHP Switcher application folder back. 94 | 95 | ## Add other PHP versions to your Xampp 96 | ### Important notes 97 | After successfully installing Xampp PHP Switcher, by default you will have a built-in PHP version of your Xampp. In order to use other versions of PHP, we will proceed to add those builds to the repository. This will also be easily done. But you need to keep in mind the following requirements: 98 | 99 | * Only add builds downloaded from the Xampp official website, or builds that have been added to the Xampp PHP Switcher repository. 100 | * Only builds with the same architecture (32 or 64 bit runtime) may be added to the current build. 101 | 102 | ### Step by step process of adding build 103 | #### Check architectural information of the current build 104 | Execute the following command: 105 | 106 | ``` 107 | $ xphp info current 108 | ``` 109 | 110 | View the information in Architecture section and remember it. 111 | 112 | #### Download compatible PHP builds 113 | * Download compatible Xampp builds from Official Download page at [here](https://sourceforge.net/projects/xampp/files/XAMPP%20Windows/). 114 | * When downloading, note that the architecture of the build to download must be the same as the current build architecture we have just seen above. 115 | * Do not download the Portable builds, nor download the installer (.exe). 116 | * After downloading, extract it to somewhere on our computer and note the path to the PHP directory inside the newly extracted Xampp directory. 117 | 118 | > Compatible items to download 119 | 120 | ![Compatible items to download](https://user-images.githubusercontent.com/9862115/72957852-a5f85600-3dd7-11ea-98d8-8c7f92a3f6ef.jpg) 121 | 122 | #### Add recent downloaded PHP build 123 | Execute the `xphp add` command: 124 | ``` 125 | $ xphp add "drive:\path\to\above\xampp-php-directory" 126 | ``` 127 | 128 | or more simply with the second syntax: 129 | ``` 130 | $ xphp add 131 | ``` 132 | And then provide the link above when requested. 133 | 134 | > _Note: If you use the first syntax, you should put the path in the double quotes (") to avoid the errors of the effect of whitespaces._ 135 | 136 | And now we have many different versions of PHP to use. We can switch between versions with the `xphp switch` command. 137 | 138 | ### Behind the process of adding PHP build 139 | When we use the `xphp add` command, Xampp PHP Switcher will do the following: 140 | 141 | * Copy the entire directory containing the PHP build into the repository located in your Xampp. 142 | * Conduct standardization of the paths in some files of new PHP build so that it to works correctly with your Xampp. 143 | * Create a file containing the PHP build archive information (with the name ".storage") and place it in new build's directory in the repository. 144 | * Create a file named "xampp-httpd-php(x).conf" (x is the corresponding PHP major-version) so that Apache in your Xampp can handle the new PHP build. Based on that, you can switch between different PHP versions. 145 | 146 | ### Update the process of adding PHP builds in the future 147 | The process of standardize paths is done based on a file named "need_standardize.lst" located in the "src" folder of project directory. Currently, this file has supported the path standardization for PHP builds from versions 5.4 (Xampp 1.8.3) to 8.0 (Xampp 8.0.x). 148 | 149 | Every time a new version of Xampp is released, this file will be checked and updated. You only need to copy the whole new content of this file and overwrite your existing file in your Xampp PHP Switcher directory. Or you proceed to update Xampp PHP Switcher according to the instructions [here](https://github.com/JackieDo/Xampp-PHP-Switcher#updation). 150 | 151 | ## Usage 152 | Because of a path to the Xampp PHP Switcher application directory has been added to the Windows Path Environment Variables during the installation process, now you can just open the terminal `(no need to open in Administrator mode anymore)` anywhere and excute one of the following `xphp` commands: 153 | 154 | #### Display the help message 155 | 156 | Syntax: 157 | ``` 158 | $ xphp help 159 | ``` 160 | 161 | #### Display information of current PHP version 162 | 163 | Syntax: 164 | ``` 165 | $ xphp info current 166 | ``` 167 | 168 | #### Display information of existing PHP version in repository 169 | 170 | Syntax: 171 | ``` 172 | $ xphp info [VERSION] 173 | ``` 174 | 175 | Example: 176 | ``` 177 | $ xphp info 5.6.40 178 | ``` 179 | 180 | > _Note: The VERSION parameter is optional. If you do not pass it to the command statement, you will also be asked to enter this information later._ 181 | 182 | #### List all existing PHP versions in repository 183 | 184 | Syntax: 185 | ``` 186 | $ xphp list 187 | ``` 188 | 189 | #### Remove an existing PHP version outof repository 190 | 191 | Syntax: 192 | ``` 193 | $ xphp remove [VERSION] 194 | ``` 195 | 196 | Example: 197 | ``` 198 | $ xphp remove 5.6.40 199 | ``` 200 | 201 | #### Add one PHP version to repository 202 | 203 | Syntax: 204 | ``` 205 | $ xphp add [PATH_TO_ANOTHER_XAMPP_PHP_DIRECTORY] 206 | ``` 207 | 208 | Example: 209 | ``` 210 | $ xphp add "D:\download\xampp-win32-5.5.19-0-VC11\php" 211 | ``` 212 | 213 | #### Switch to specific PHP version to use 214 | 215 | Syntax: 216 | ``` 217 | $ xphp switch [VERSION] 218 | ``` 219 | 220 | Example: 221 | ``` 222 | $ xphp switch 5.5.19 223 | ``` 224 | 225 | ## The file `php.ini` editing 226 | Once you have added the various PHP versions to your Xampp, they will be put in a repository located in a directory named "phpRepository" located in your Xampp directory. And they will have separate "php.ini" configuration files. If you need to edit the configuration for PHP, you must edit each "php.ini" file for each corresponding version in the repository. 227 | 228 | ## License 229 | [MIT](LICENSE) © Jackie Do 230 | -------------------------------------------------------------------------------- /src/power_exec.vbs: -------------------------------------------------------------------------------- 1 | Option Explicit 2 | 3 | ' Define global variables 4 | Dim objArgs: Set objArgs = WScript.Arguments 5 | Dim objShell: Set objShell = CreateObject("WScript.Shell") 6 | Dim objFSO: Set objFSO = CreateObject("Scripting.FileSystemObject") 7 | Dim scriptBaseName: scriptBaseName = objFSO.GetBaseName(WScript.ScriptName) 8 | Dim scriptExt: scriptExt = LCase(objFSO.GetExtensionName(WScript.ScriptName)) 9 | 10 | ' Define subroutines 11 | Private Sub ClearVars() 12 | Set objFSO = Nothing 13 | Set objShell = Nothing 14 | Set objArgs = Nothing 15 | End Sub 16 | 17 | Private Sub ShowHelp() 18 | Dim callMethod 19 | 20 | If (scriptExt = "vbs") Then 21 | callMethod = "cscript " & WScript.ScriptName 22 | Else 23 | callMethod = scriptBaseName 24 | End If 25 | 26 | Dim helpMsg 27 | 28 | helpMsg = "" & _ 29 | vbNewLine & "Usage:" & _ 30 | vbNewLine & " " & callMethod & " [-k] [-p] [-w] [-e] [(-m | -x)] [-i] [-n] [-h] " & _ 31 | vbNewLine & _ 32 | vbNewLine & "Options:" & _ 33 | vbNewLine & " -k Keep the command prompt window after execution; equivalent to ""cmd /k command""." & _ 34 | vbNewLine & " -p Change working directory to the current one before executing the command." & _ 35 | vbNewLine & " -w Wait until the execution is terminated and the execution window is closed." & _ 36 | vbNewLine & " -e Execute command with elevated permission (run as Administrator)." & _ 37 | vbNewLine & " -m Minimize command execution window (visible mode)." & _ 38 | vbNewLine & " -x Maximize command execution window (visible mode)." & _ 39 | vbNewLine & " -i Hide command execution window (invisible mode)." & _ 40 | vbNewLine & " -n Do not present " & scriptBaseName & "'s messages (error or success) except the help." & _ 41 | vbNewLine & " -h Display " & scriptBaseName & "'s help message." & _ 42 | vbNewLine & _ 43 | vbNewLine & "Notes:" & _ 44 | vbNewLine & " - The [-k] parameter will not work if the application will be executed" & _ 45 | vbNewLine & " actively closing the window." & _ 46 | vbNewLine & " - Cannot use the [-m] and [-x] parameters at the same time." & _ 47 | vbNewLine & " - Using the parameter [-i] will omit the parameters [-m] and [-x]." & _ 48 | vbNewLine & " - Using the [-w] and [-i] parameters together requires the assurance of" & _ 49 | vbNewLine & " completion of the application will be executed. Be careful when using." & _ 50 | vbNewLine 51 | 52 | WScript.Echo helpMsg 53 | End Sub 54 | 55 | Private Sub ShowHelpWithMessage(ByVal message, ByVal showAs) 56 | If ((Not message = vbNullString) and (Not message = vbNull)) Then 57 | WriteMsg message, showAs 58 | End If 59 | 60 | ShowHelp 61 | End Sub 62 | 63 | Private Sub Quit(errorCode) 64 | ClearVars 65 | WScript.Quit errorCode 66 | End Sub 67 | 68 | Private Sub WriteMsg(ByVal message, ByVal writeAs) 69 | If (DetectScriptMode = "Window") Then 70 | MsgBox message 71 | Else 72 | Select Case writeAs 73 | Case "output" 74 | WScript.StdOut.WriteLine message 75 | Case "error" 76 | WScript.StdErr.WriteLine message 77 | Case "echo" 78 | WScript.Echo message 79 | End Select 80 | End If 81 | End Sub 82 | 83 | ' Define functions 84 | Private Function ParseArguments(ByVal arguments) 85 | Dim isElevateOption: isElevateOption = true 86 | Dim objParams: Set objParams = CreateObject("Scripting.Dictionary") 87 | objParams.CompareMode = vbTextCompare 88 | 89 | objParams.Add "CmdOptions", "/C" 90 | objParams.Add "ShowWindow", true 91 | objParams.Add "WindowSize", "Normal" 92 | objParams.Add "WaitForFinish", false 93 | objParams.Add "PushdCurrDir", false 94 | objParams.Add "Elevated", false 95 | objParams.Add "NoOutput", false 96 | objParams.Add "Command", "" 97 | 98 | Dim i 99 | For i = 0 To arguments.Count - 1 100 | Dim currentArg: currentArg = arguments.item(i) 101 | Dim argFirstChar: argFirstChar = Left(currentArg, 1) 102 | Dim argRemainder: argRemainder = Mid(currentArg, 2) 103 | Dim lcaseCurrArg: lcaseCurrArg = LCase(currentArg) 104 | 105 | If (isElevateOption and (not argFirstChar = "/") and (not argFirstChar = "-")) Then 106 | isElevateOption = false 107 | End If 108 | 109 | If (isElevateOption) Then 110 | If (lcaseCurrArg = "/?" or lcaseCurrArg = "/h" or lcaseCurrArg = "/help" or lcaseCurrArg = "-h" or lcaseCurrArg = "--help") Then 111 | ShowHelpWithMessage "Execute commands with more advanced controls.", "success" 112 | Quit 0 113 | End If 114 | 115 | Select Case LCase(argRemainder) 116 | Case "k" 117 | objParams("CmdOptions") = Replace(objParams("CmdOptions"), "/C", "/K") 118 | Case "p" 119 | objParams("PushdCurrDir") = true 120 | Case "w" 121 | objParams("WaitForFinish") = true 122 | Case "e" 123 | objParams("Elevated") = true 124 | Case "m" 125 | If (Not objParams("WindowSize") = "Normal") Then 126 | ShowHelpWithMessage scriptBaseName & " error: Cannot use the [-m] and [-x] parameters at the same time. Cancel the operation.", "error" 127 | Quit 1 128 | End If 129 | 130 | objParams("WindowSize") = "Minimized" 131 | Case "x" 132 | If (Not objParams("WindowSize") = "Normal") Then 133 | ShowHelpWithMessage scriptBaseName & " error: Cannot use the [-m] and [-x] parameters at the same time. Cancel the operation.", "error" 134 | Quit 1 135 | End If 136 | 137 | objParams("WindowSize") = "Maximized" 138 | Case "i" 139 | objParams("ShowWindow") = false 140 | Case "n" 141 | objParams("NoOutput") = true 142 | Case Else 143 | ShowHelpWithMessage scriptBaseName & " error: Unknown argument """ & currentArg & """. Cancel the operation.", "error" 144 | Quit 1 145 | End Select 146 | Else 147 | If (InStr(currentArg, " ")) Then 148 | currentArg = """" & currentArg & """" 149 | End If 150 | 151 | If (objParams("Command") = "") Then 152 | objParams("Command") = currentArg 153 | Else 154 | objParams("Command") = objParams("Command") & " " & currentArg 155 | End If 156 | End If 157 | Next 158 | 159 | Set ParseArguments = objParams 160 | End Function 161 | 162 | Private Function DetectScriptMode() 163 | Dim scriptMode 164 | 165 | If InStr(1, WScript.FullName, "cscript", vbTextCompare) Then 166 | scriptMode = "Console" 167 | ElseIf InStr(1, WScript.FullName, "wscript", vbTextCompare) Then 168 | scriptMode = "Window" 169 | Else 170 | scriptMode = "Unknown" 171 | End If 172 | 173 | DetectScriptMode = scriptMode 174 | End Function 175 | 176 | Private Function RandomString() 177 | Randomize() 178 | 179 | Dim CharacterSetArray 180 | CharacterSetArray = Array(_ 181 | Array(7, "abcdefghijklmnopqrstuvwxyz"), _ 182 | Array(1, "0123456789") _ 183 | ) 184 | 185 | Dim i 186 | Dim j 187 | Dim Count 188 | Dim Chars 189 | Dim Index 190 | Dim Temp 191 | 192 | For i = 0 to UBound(CharacterSetArray) 193 | Count = CharacterSetArray(i)(0) 194 | Chars = CharacterSetArray(i)(1) 195 | 196 | For j = 1 to Count 197 | Index = Int(Rnd() * Len(Chars)) + 1 198 | Temp = Temp & Mid(Chars, Index, 1) 199 | Next 200 | Next 201 | 202 | Dim TempCopy 203 | 204 | Do Until Len(Temp) = 0 205 | Index = Int(Rnd() * Len(Temp)) + 1 206 | TempCopy = TempCopy & Mid(Temp, Index, 1) 207 | Temp = Mid(Temp, 1, Index - 1) & Mid(Temp, Index + 1) 208 | Loop 209 | 210 | RandomString = TempCopy 211 | End Function 212 | 213 | '''''''''''''''' START MAIN PROGRAM '''''''''''''''' 214 | 215 | ' Notice if runnig in Window mode 216 | If (DetectScriptMode() = "Window") Then 217 | MsgBox "Sorry! This application only runs in console mode." & vbNewLine & _ 218 | "Please run this application with the ""cscript"" command." 219 | Quit 1 220 | End If 221 | 222 | ' Arguments is missing 223 | If (objArgs.Count <= 0) Then 224 | ShowHelpWithMessage scriptBaseName & " error: Missing input arguments.", "error" 225 | Quit 1 226 | End If 227 | 228 | ' Parse arguments 229 | Dim objParams: Set objParams = ParseArguments(objArgs) 230 | 231 | ' The command argument is missing 232 | If (Trim(objParams("Command")) = "") Then 233 | ShowHelpWithMessage scriptBaseName & " error: The ""command"" argument is missing.", "error" 234 | Set objParams = Nothing 235 | Quit 1 236 | End If 237 | 238 | ' Push current dir before execute command 239 | If (objParams("PushdCurrDir")) Then 240 | objParams("Command") = "pushd """ & objShell.CurrentDirectory & """" & " & " & objParams("Command") 241 | End If 242 | 243 | ' Prepare error lof file if the execution of command occurred error 244 | Dim tempFolder: tempFolder = objShell.ExpandEnvironmentStrings("%TEMP%") 245 | Dim errorLogPath: errorLogPath = tempFolder & "\" & LCase(scriptBaseName) & "-error-" & RandomString() & ".log" 246 | objParams("Command") = objParams("Command") & " || type nul>""" & errorLogPath & """" 247 | 248 | ' Build powershell command 249 | objParams("Command") = Replace(objParams("Command"), """", "\""") 250 | 251 | Dim powerShellWait 252 | If (objParams("WaitForFinish")) Then 253 | powerShellWait = " -Wait" 254 | Else 255 | powerShellWait = "" 256 | End If 257 | 258 | Dim powerShellWindow 259 | If (objParams("ShowWindow")) Then 260 | powerShellWindow = " -WindowStyle " & objParams("WindowSize") 261 | Else 262 | powerShellWindow = " -WindowStyle Hidden" 263 | End If 264 | 265 | Dim powerShellRunAs 266 | If (objParams("Elevated")) Then 267 | powerShellRunAs = " -Verb RunAs" 268 | Else 269 | powerShellRunAs = "" 270 | End If 271 | 272 | Dim powerShellCommand 273 | powerShellCommand = "powershell -Command ""Start-Process cmd -ArgumentList {""" & objParams("CmdOptions") & " \""" & objParams("Command") & "\""""}" & _ 274 | powerShellRunAs & powerShellWait & powerShellWindow & """" 275 | 276 | ' Run powershell command 277 | ' Method 1: Use Run function if we do not need to store the process ID 278 | Dim exitCode: exitCode = objShell.Run(powerShellCommand, 0, true) 279 | 280 | If (exitCode = 1) Then 281 | If (Not objParams("NoOutput")) Then 282 | WriteMsg "The operation was canceled by user.", "error" 283 | End If 284 | 285 | Set objParams = Nothing 286 | Quit 1 287 | End If 288 | 289 | ' Method 2: Use Exec function if want to store process ID 290 | ' Dim objExec: Set objExec = objShell.Exec(powerShellCommand) 291 | ' Dim processID: processID = objExec.ProcessID 292 | ' While (objExec.Status = 0) 293 | ' WScript.Sleep 50 294 | ' If (objExec.ExitCode = 1) Then 295 | ' If (Not objParams("NoOutput")) Then 296 | ' WriteMsg "The operation was canceled by the user.", "error" 297 | ' End If 298 | ' Set objExec = Nothing 299 | ' Quit 1 300 | ' End If 301 | ' Wend 302 | ' Set objExec = Nothing 303 | 304 | ' If do not wait for finish 305 | If (Not objParams("WaitForFinish")) Then 306 | If (Not objParams("NoOutput")) Then 307 | WriteMsg "The operation has been executed.", "output" 308 | End If 309 | 310 | Set objParams = Nothing 311 | Quit 0 312 | End If 313 | 314 | ' If the execution of command occurred error 315 | If (objFSO.FileExists(errorLogPath)) Then 316 | objFSO.DeleteFile(errorLogPath) 317 | 318 | If (Not objParams("NoOutput")) Then 319 | WriteMsg "The execution of the operation occurred error.", "error" 320 | End If 321 | 322 | Set objParams = Nothing 323 | Quit 1 324 | End If 325 | 326 | ' Finish command excution 327 | If (Not objParams("NoOutput")) Then 328 | WriteMsg "The execution of the operation has been successfully completed.", "output" 329 | End If 330 | 331 | Set objParams = Nothing 332 | Quit 0 333 | 334 | '''''''''''''''' END MAIN PROGRAM '''''''''''''''' -------------------------------------------------------------------------------- /vendor/composer/ClassLoader.php: -------------------------------------------------------------------------------- 1 | 7 | * Jordi Boggiano 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Composer\Autoload; 14 | 15 | /** 16 | * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. 17 | * 18 | * $loader = new \Composer\Autoload\ClassLoader(); 19 | * 20 | * // register classes with namespaces 21 | * $loader->add('Symfony\Component', __DIR__.'/component'); 22 | * $loader->add('Symfony', __DIR__.'/framework'); 23 | * 24 | * // activate the autoloader 25 | * $loader->register(); 26 | * 27 | * // to enable searching the include path (eg. for PEAR packages) 28 | * $loader->setUseIncludePath(true); 29 | * 30 | * In this example, if you try to use a class in the Symfony\Component 31 | * namespace or one of its children (Symfony\Component\Console for instance), 32 | * the autoloader will first look for the class under the component/ 33 | * directory, and it will then fallback to the framework/ directory if not 34 | * found before giving up. 35 | * 36 | * This class is loosely based on the Symfony UniversalClassLoader. 37 | * 38 | * @author Fabien Potencier 39 | * @author Jordi Boggiano 40 | * @see http://www.php-fig.org/psr/psr-0/ 41 | * @see http://www.php-fig.org/psr/psr-4/ 42 | */ 43 | class ClassLoader 44 | { 45 | // PSR-4 46 | private $prefixLengthsPsr4 = array(); 47 | private $prefixDirsPsr4 = array(); 48 | private $fallbackDirsPsr4 = array(); 49 | 50 | // PSR-0 51 | private $prefixesPsr0 = array(); 52 | private $fallbackDirsPsr0 = array(); 53 | 54 | private $useIncludePath = false; 55 | private $classMap = array(); 56 | private $classMapAuthoritative = false; 57 | private $missingClasses = array(); 58 | private $apcuPrefix; 59 | 60 | public function getPrefixes() 61 | { 62 | if (!empty($this->prefixesPsr0)) { 63 | return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); 64 | } 65 | 66 | return array(); 67 | } 68 | 69 | public function getPrefixesPsr4() 70 | { 71 | return $this->prefixDirsPsr4; 72 | } 73 | 74 | public function getFallbackDirs() 75 | { 76 | return $this->fallbackDirsPsr0; 77 | } 78 | 79 | public function getFallbackDirsPsr4() 80 | { 81 | return $this->fallbackDirsPsr4; 82 | } 83 | 84 | public function getClassMap() 85 | { 86 | return $this->classMap; 87 | } 88 | 89 | /** 90 | * @param array $classMap Class to filename map 91 | */ 92 | public function addClassMap(array $classMap) 93 | { 94 | if ($this->classMap) { 95 | $this->classMap = array_merge($this->classMap, $classMap); 96 | } else { 97 | $this->classMap = $classMap; 98 | } 99 | } 100 | 101 | /** 102 | * Registers a set of PSR-0 directories for a given prefix, either 103 | * appending or prepending to the ones previously set for this prefix. 104 | * 105 | * @param string $prefix The prefix 106 | * @param array|string $paths The PSR-0 root directories 107 | * @param bool $prepend Whether to prepend the directories 108 | */ 109 | public function add($prefix, $paths, $prepend = false) 110 | { 111 | if (!$prefix) { 112 | if ($prepend) { 113 | $this->fallbackDirsPsr0 = array_merge( 114 | (array) $paths, 115 | $this->fallbackDirsPsr0 116 | ); 117 | } else { 118 | $this->fallbackDirsPsr0 = array_merge( 119 | $this->fallbackDirsPsr0, 120 | (array) $paths 121 | ); 122 | } 123 | 124 | return; 125 | } 126 | 127 | $first = $prefix[0]; 128 | if (!isset($this->prefixesPsr0[$first][$prefix])) { 129 | $this->prefixesPsr0[$first][$prefix] = (array) $paths; 130 | 131 | return; 132 | } 133 | if ($prepend) { 134 | $this->prefixesPsr0[$first][$prefix] = array_merge( 135 | (array) $paths, 136 | $this->prefixesPsr0[$first][$prefix] 137 | ); 138 | } else { 139 | $this->prefixesPsr0[$first][$prefix] = array_merge( 140 | $this->prefixesPsr0[$first][$prefix], 141 | (array) $paths 142 | ); 143 | } 144 | } 145 | 146 | /** 147 | * Registers a set of PSR-4 directories for a given namespace, either 148 | * appending or prepending to the ones previously set for this namespace. 149 | * 150 | * @param string $prefix The prefix/namespace, with trailing '\\' 151 | * @param array|string $paths The PSR-4 base directories 152 | * @param bool $prepend Whether to prepend the directories 153 | * 154 | * @throws \InvalidArgumentException 155 | */ 156 | public function addPsr4($prefix, $paths, $prepend = false) 157 | { 158 | if (!$prefix) { 159 | // Register directories for the root namespace. 160 | if ($prepend) { 161 | $this->fallbackDirsPsr4 = array_merge( 162 | (array) $paths, 163 | $this->fallbackDirsPsr4 164 | ); 165 | } else { 166 | $this->fallbackDirsPsr4 = array_merge( 167 | $this->fallbackDirsPsr4, 168 | (array) $paths 169 | ); 170 | } 171 | } elseif (!isset($this->prefixDirsPsr4[$prefix])) { 172 | // Register directories for a new namespace. 173 | $length = strlen($prefix); 174 | if ('\\' !== $prefix[$length - 1]) { 175 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 176 | } 177 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 178 | $this->prefixDirsPsr4[$prefix] = (array) $paths; 179 | } elseif ($prepend) { 180 | // Prepend directories for an already registered namespace. 181 | $this->prefixDirsPsr4[$prefix] = array_merge( 182 | (array) $paths, 183 | $this->prefixDirsPsr4[$prefix] 184 | ); 185 | } else { 186 | // Append directories for an already registered namespace. 187 | $this->prefixDirsPsr4[$prefix] = array_merge( 188 | $this->prefixDirsPsr4[$prefix], 189 | (array) $paths 190 | ); 191 | } 192 | } 193 | 194 | /** 195 | * Registers a set of PSR-0 directories for a given prefix, 196 | * replacing any others previously set for this prefix. 197 | * 198 | * @param string $prefix The prefix 199 | * @param array|string $paths The PSR-0 base directories 200 | */ 201 | public function set($prefix, $paths) 202 | { 203 | if (!$prefix) { 204 | $this->fallbackDirsPsr0 = (array) $paths; 205 | } else { 206 | $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; 207 | } 208 | } 209 | 210 | /** 211 | * Registers a set of PSR-4 directories for a given namespace, 212 | * replacing any others previously set for this namespace. 213 | * 214 | * @param string $prefix The prefix/namespace, with trailing '\\' 215 | * @param array|string $paths The PSR-4 base directories 216 | * 217 | * @throws \InvalidArgumentException 218 | */ 219 | public function setPsr4($prefix, $paths) 220 | { 221 | if (!$prefix) { 222 | $this->fallbackDirsPsr4 = (array) $paths; 223 | } else { 224 | $length = strlen($prefix); 225 | if ('\\' !== $prefix[$length - 1]) { 226 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 227 | } 228 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 229 | $this->prefixDirsPsr4[$prefix] = (array) $paths; 230 | } 231 | } 232 | 233 | /** 234 | * Turns on searching the include path for class files. 235 | * 236 | * @param bool $useIncludePath 237 | */ 238 | public function setUseIncludePath($useIncludePath) 239 | { 240 | $this->useIncludePath = $useIncludePath; 241 | } 242 | 243 | /** 244 | * Can be used to check if the autoloader uses the include path to check 245 | * for classes. 246 | * 247 | * @return bool 248 | */ 249 | public function getUseIncludePath() 250 | { 251 | return $this->useIncludePath; 252 | } 253 | 254 | /** 255 | * Turns off searching the prefix and fallback directories for classes 256 | * that have not been registered with the class map. 257 | * 258 | * @param bool $classMapAuthoritative 259 | */ 260 | public function setClassMapAuthoritative($classMapAuthoritative) 261 | { 262 | $this->classMapAuthoritative = $classMapAuthoritative; 263 | } 264 | 265 | /** 266 | * Should class lookup fail if not found in the current class map? 267 | * 268 | * @return bool 269 | */ 270 | public function isClassMapAuthoritative() 271 | { 272 | return $this->classMapAuthoritative; 273 | } 274 | 275 | /** 276 | * APCu prefix to use to cache found/not-found classes, if the extension is enabled. 277 | * 278 | * @param string|null $apcuPrefix 279 | */ 280 | public function setApcuPrefix($apcuPrefix) 281 | { 282 | $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; 283 | } 284 | 285 | /** 286 | * The APCu prefix in use, or null if APCu caching is not enabled. 287 | * 288 | * @return string|null 289 | */ 290 | public function getApcuPrefix() 291 | { 292 | return $this->apcuPrefix; 293 | } 294 | 295 | /** 296 | * Registers this instance as an autoloader. 297 | * 298 | * @param bool $prepend Whether to prepend the autoloader or not 299 | */ 300 | public function register($prepend = false) 301 | { 302 | spl_autoload_register(array($this, 'loadClass'), true, $prepend); 303 | } 304 | 305 | /** 306 | * Unregisters this instance as an autoloader. 307 | */ 308 | public function unregister() 309 | { 310 | spl_autoload_unregister(array($this, 'loadClass')); 311 | } 312 | 313 | /** 314 | * Loads the given class or interface. 315 | * 316 | * @param string $class The name of the class 317 | * @return bool|null True if loaded, null otherwise 318 | */ 319 | public function loadClass($class) 320 | { 321 | if ($file = $this->findFile($class)) { 322 | includeFile($file); 323 | 324 | return true; 325 | } 326 | } 327 | 328 | /** 329 | * Finds the path to the file where the class is defined. 330 | * 331 | * @param string $class The name of the class 332 | * 333 | * @return string|false The path if found, false otherwise 334 | */ 335 | public function findFile($class) 336 | { 337 | // class map lookup 338 | if (isset($this->classMap[$class])) { 339 | return $this->classMap[$class]; 340 | } 341 | if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { 342 | return false; 343 | } 344 | if (null !== $this->apcuPrefix) { 345 | $file = apcu_fetch($this->apcuPrefix.$class, $hit); 346 | if ($hit) { 347 | return $file; 348 | } 349 | } 350 | 351 | $file = $this->findFileWithExtension($class, '.php'); 352 | 353 | // Search for Hack files if we are running on HHVM 354 | if (false === $file && defined('HHVM_VERSION')) { 355 | $file = $this->findFileWithExtension($class, '.hh'); 356 | } 357 | 358 | if (null !== $this->apcuPrefix) { 359 | apcu_add($this->apcuPrefix.$class, $file); 360 | } 361 | 362 | if (false === $file) { 363 | // Remember that this class does not exist. 364 | $this->missingClasses[$class] = true; 365 | } 366 | 367 | return $file; 368 | } 369 | 370 | private function findFileWithExtension($class, $ext) 371 | { 372 | // PSR-4 lookup 373 | $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; 374 | 375 | $first = $class[0]; 376 | if (isset($this->prefixLengthsPsr4[$first])) { 377 | $subPath = $class; 378 | while (false !== $lastPos = strrpos($subPath, '\\')) { 379 | $subPath = substr($subPath, 0, $lastPos); 380 | $search = $subPath . '\\'; 381 | if (isset($this->prefixDirsPsr4[$search])) { 382 | $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); 383 | foreach ($this->prefixDirsPsr4[$search] as $dir) { 384 | if (file_exists($file = $dir . $pathEnd)) { 385 | return $file; 386 | } 387 | } 388 | } 389 | } 390 | } 391 | 392 | // PSR-4 fallback dirs 393 | foreach ($this->fallbackDirsPsr4 as $dir) { 394 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { 395 | return $file; 396 | } 397 | } 398 | 399 | // PSR-0 lookup 400 | if (false !== $pos = strrpos($class, '\\')) { 401 | // namespaced class name 402 | $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) 403 | . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); 404 | } else { 405 | // PEAR-like class name 406 | $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; 407 | } 408 | 409 | if (isset($this->prefixesPsr0[$first])) { 410 | foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { 411 | if (0 === strpos($class, $prefix)) { 412 | foreach ($dirs as $dir) { 413 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 414 | return $file; 415 | } 416 | } 417 | } 418 | } 419 | } 420 | 421 | // PSR-0 fallback dirs 422 | foreach ($this->fallbackDirsPsr0 as $dir) { 423 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 424 | return $file; 425 | } 426 | } 427 | 428 | // PSR-0 include paths. 429 | if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { 430 | return $file; 431 | } 432 | 433 | return false; 434 | } 435 | } 436 | 437 | /** 438 | * Scope isolated include. 439 | * 440 | * Prevents access to $this/self from included files. 441 | */ 442 | function includeFile($file) 443 | { 444 | include $file; 445 | } 446 | -------------------------------------------------------------------------------- /src/Support/VersionRepository.php: -------------------------------------------------------------------------------- 1 | setDebug($debugStatus); 20 | $this->setArchitecture($architecture); 21 | $this->setLocation($location); 22 | } 23 | 24 | public function __get($propertyName) 25 | { 26 | if (property_exists(__CLASS__, $propertyName)) { 27 | $reflection = new ReflectionProperty(__CLASS__, $propertyName); 28 | 29 | if ($reflection->isPrivate()) { 30 | return null; 31 | } 32 | 33 | return $this->$propertyName; 34 | } 35 | 36 | return null; 37 | } 38 | 39 | public function setArchitecture($architecture) 40 | { 41 | $this->architecture = $architecture; 42 | 43 | return $this; 44 | } 45 | 46 | public function setLocation($dirPath = null) 47 | { 48 | if (! empty($dirPath)) { 49 | $dirPath = strval($dirPath); 50 | 51 | if (! is_dir($dirPath)) { 52 | @mkdir($dirPath, 0755, true); 53 | } 54 | 55 | $this->location = realpath($dirPath); 56 | 57 | return $this->loadVersions(); 58 | } 59 | 60 | return $this; 61 | } 62 | 63 | public function setDebug($status) 64 | { 65 | if (is_bool($status)) { 66 | $this->debug = $status; 67 | } 68 | 69 | return $this; 70 | } 71 | 72 | public function buildStoragePath($version) 73 | { 74 | if (is_null($this->location)) { 75 | if ($this->debug) { 76 | throw new Exception('The "location" property has not been set.'); 77 | } 78 | 79 | return null; 80 | } 81 | 82 | $storageName = str_replace('{{version}}', $version, self::STORAGE_NAME_PATTERN); 83 | $storagePath = $this->location . DS . $storageName; 84 | 85 | return $storagePath; 86 | } 87 | 88 | public function mapToUse($version, $usePath) 89 | { 90 | $targetSymlink = $this->versions[$version]['storagePath']; 91 | 92 | if ($usePath == readlink($usePath)) { 93 | if ($this->debug) { 94 | throw new Exception('The parameter "usePath" is currently a real directory.'); 95 | } 96 | 97 | return false; 98 | } 99 | 100 | $output = $status = null; 101 | exec('rd "' . $usePath . '"', $output, $status); 102 | 103 | if ($status == 1) { 104 | if ($this->debug) { 105 | throw new Exception('Cannot remove old symbolic link.'); 106 | } 107 | 108 | return false; 109 | } 110 | 111 | $output = $status = null; 112 | exec('mklink /J "' . $usePath . '" "' . $targetSymlink . '"', $output, $status); 113 | 114 | if ($status == 1) { 115 | if ($this->debug) { 116 | throw new Exception('The creating symbolic link has failed.'); 117 | } 118 | 119 | return false; 120 | } 121 | 122 | return true; 123 | } 124 | 125 | public function importBuild($source, $predefined = [], $validityCheck = true) 126 | { 127 | if ($validityCheck) { 128 | if (! maybe_phpdir($source)) { 129 | if ($this->debug) { 130 | throw new Exception('The directory "' . $source . '" is not a PHP directory.'); 131 | } 132 | 133 | return [ 134 | 'error' => 1, 135 | 'data' => [] 136 | ]; 137 | } 138 | } 139 | 140 | $architecture = (array_key_exists('architecture', $predefined)) ? $predefined['architecture'] : get_architecture_phpdir($source); 141 | if ($validityCheck) { 142 | if (!$this->architecture != $architecture) { 143 | if ($this->debug) { 144 | throw new Exception('The build at "' . $source . '" is not compatible with this storage architecture'); 145 | } 146 | 147 | return [ 148 | 'error' => 2, 149 | 'data' => [] 150 | ]; 151 | } 152 | } 153 | 154 | $version = (array_key_exists('version', $predefined)) ? $predefined['version'] : get_version_phpdir($source); 155 | if ($validityCheck) { 156 | if (array_key_exists($version, $this->versions)) { 157 | if ($this->debug) { 158 | throw new Exception('Version of the build at "' . $source . '" coincides with the existing version.'); 159 | } 160 | 161 | return [ 162 | 'error' => 3, 163 | 'data' => [] 164 | ]; 165 | } 166 | } 167 | 168 | $compiler = (array_key_exists('compiler', $predefined)) ? $predefined['compiler'] : get_compiler_phpdir($source); 169 | $buildDate = (array_key_exists('buildDate', $predefined)) ? $predefined['buildDate'] : get_builddate_phpdir($source); 170 | $zendVersion = (array_key_exists('zendVersion', $predefined)) ? $predefined['zendVersion'] : get_zendversion_phpdir($source); 171 | $storagePath = $this->buildStoragePath($version); 172 | 173 | if (! $storagePath) { 174 | return [ 175 | 'error' => 4, 176 | 'data' => [] 177 | ]; 178 | } 179 | 180 | exec('xcopy /E /I /H /Y /Q "' . $source . '" "' . $storagePath . '"', $output, $status); 181 | 182 | if ($status != 0) { 183 | if ($this->debug) { 184 | throw new Exception('The copy process has failed.'); 185 | } 186 | 187 | return [ 188 | 'error' => 5, 189 | 'data' => [] 190 | ]; 191 | } 192 | 193 | $storageConfig = $this->getStorageConfig($storagePath); 194 | 195 | $storageConfig['BuildInfo']['Version'] = $version; 196 | $storageConfig['BuildInfo']['Architecture'] = $architecture; 197 | $storageConfig['BuildInfo']['Compiler'] = $compiler; 198 | $storageConfig['BuildInfo']['BuildDate'] = $buildDate; 199 | $storageConfig['BuildInfo']['ZendVersion'] = $zendVersion; 200 | $storageConfig['RepoImporting']['AddOnBuild'] = true; 201 | 202 | $this->saveStorageConfig($storagePath, $storageConfig); 203 | 204 | $this->versions[$version] = [ 205 | 'storagePath' => $storagePath, 206 | 'architecture' => $architecture, 207 | 'compiler' => $compiler, 208 | 'buildDate' => $buildDate, 209 | 'zendVersion' => $zendVersion, 210 | 'isAddOnBuild' => true 211 | ]; 212 | 213 | return [ 214 | 'error' => 0, 215 | 'data' => [ 216 | 'storagePath' => $storagePath, 217 | 'storageConfig' => $storageConfig 218 | ] 219 | ]; 220 | } 221 | 222 | public function removeBuild($version, $validityCheck = true) 223 | { 224 | if ($validityCheck) { 225 | if (! $this->has($version)) { 226 | if ($this->debug) { 227 | throw new Exception('The version "' . $version . '" does not exist.'); 228 | } 229 | 230 | return false; 231 | } 232 | 233 | if (! $this->isAddOnBuild($version)) { 234 | if ($this->debug) { 235 | throw new Exception('The version "' . $version . '" is not an imported build.'); 236 | } 237 | 238 | return false; 239 | } 240 | } 241 | 242 | $storagePath = $this->versions[$version]['storagePath']; 243 | exec('rd /Q /S "' . $storagePath . '"', $output, $status); 244 | 245 | if ($status != 0) { 246 | if ($this->debug) { 247 | throw new Exception('The process of deleting the directory "' . $storagePath . '" has an error'); 248 | } 249 | 250 | return false; 251 | } 252 | 253 | unset($this->versions[$version]); 254 | 255 | return true; 256 | } 257 | 258 | public function editBuild($version, $actions = [], $validityCheck = true) 259 | { 260 | if ($validityCheck) { 261 | if (! $this->has($version)) { 262 | if ($this->debug) { 263 | throw new Exception('The version "' . $version . '" does not exist.'); 264 | } 265 | 266 | return false; 267 | } 268 | 269 | if (! $this->isAddOnBuild($version)) { 270 | if ($this->debug) { 271 | throw new Exception('The version "' . $version . '" is not an imported build. No editing allowed.'); 272 | } 273 | 274 | return false; 275 | } 276 | } 277 | 278 | $storagePath = $this->versions[$version]['storagePath']; 279 | 280 | foreach ($actions as $action) { 281 | switch ($action['type']) { 282 | case 'new': 283 | foreach ($action['files'] as $file) { 284 | $filePath = $storagePath . DS . $file; 285 | 286 | if (! is_file($filePath)) { 287 | @file_put_contents($filePath, $action['content']); 288 | } 289 | } 290 | 291 | break; 292 | 293 | case 'remove': 294 | foreach ($action['files'] as $file) { 295 | $filePath = $storagePath . DS . $file; 296 | 297 | if (is_file($filePath)) { 298 | @unlink($filePath); 299 | } 300 | } 301 | 302 | break; 303 | 304 | case 'replace': 305 | foreach ($action['files'] as $file) { 306 | $filePath = $storagePath . DS . $file; 307 | 308 | if (is_file($filePath)) { 309 | if (! array_key_exists('pattern', $action) || is_null($action['pattern']) || trim($action['pattern']) === '') { 310 | @file_put_contents($filePath, $action['replacement']); 311 | } else { 312 | $fileContent = @preg_replace($action['pattern'], $action['replacement'], @file_get_contents($filePath)); 313 | @file_put_contents($filePath, $fileContent); 314 | } 315 | } 316 | } 317 | 318 | break; 319 | 320 | default: 321 | break; 322 | } 323 | } 324 | 325 | return true; 326 | } 327 | 328 | public function has($version) 329 | { 330 | return array_key_exists($version, $this->versions); 331 | } 332 | 333 | public function isAddOnBuild($version) 334 | { 335 | return $this->versions[$version]['isAddOnBuild']; 336 | } 337 | 338 | public function getStorageConfig($storagePath) 339 | { 340 | if (is_file($storagePath . '\.storage')) { 341 | $data = @file_get_contents($storagePath . '\.storage'); 342 | 343 | if ($data) { 344 | return json_decode($data, true); 345 | } 346 | } 347 | 348 | return [ 349 | 'BuildInfo' => [], 350 | 'RepoImporting' => [] 351 | ]; 352 | } 353 | 354 | public function saveStorageConfig($storagePath, $contents = []) 355 | { 356 | return @file_put_contents($storagePath . '\.storage', json_encode($contents, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)); 357 | } 358 | 359 | private function loadVersions() 360 | { 361 | $location = $this->location; 362 | 363 | // Open repository location 364 | if (! $handle = @opendir($location)) { 365 | if ($this->debug) { 366 | throw new Exception('Could not open the directory of repository.'); 367 | } 368 | 369 | return $this; 370 | } 371 | 372 | $avaiableVersions = []; 373 | 374 | // Read all matching directories that contain PHP build 375 | while ($item = readdir($handle)) { 376 | if (($item == '.') || ($item == '..') || ($item == 'php') || (!maybe_phpdir($location . DS . $item))) { 377 | continue; 378 | } 379 | 380 | $storagePath = $location . DS . $item; 381 | $storageConfig = $this->getStorageConfig($storagePath); 382 | $hasBuildInfo = array_key_exists('BuildInfo', $storageConfig); 383 | $needUpdateInfo = false; 384 | 385 | if (!$hasBuildInfo || !array_key_exists('Version', $storageConfig['BuildInfo'])) { 386 | $storageConfig['BuildInfo']['Version'] = get_version_phpdir($storagePath); 387 | $needUpdateInfo = true; 388 | } 389 | 390 | if (!$hasBuildInfo || !array_key_exists('Architecture', $storageConfig['BuildInfo'])) { 391 | $storageConfig['BuildInfo']['Architecture'] = get_architecture_phpdir($storagePath); 392 | $needUpdateInfo = true; 393 | } 394 | 395 | if ($storageConfig['BuildInfo']['Architecture'] != $this->architecture) { 396 | continue; 397 | } 398 | 399 | if (!$hasBuildInfo || !array_key_exists('Compiler', $storageConfig['BuildInfo'])) { 400 | $storageConfig['BuildInfo']['Compiler'] = get_compiler_phpdir($storagePath); 401 | $needUpdateInfo = true; 402 | } 403 | 404 | if (!$hasBuildInfo || !array_key_exists('BuildDate', $storageConfig['BuildInfo'])) { 405 | $storageConfig['BuildInfo']['BuildDate'] = get_builddate_phpdir($storagePath); 406 | $needUpdateInfo = true; 407 | } 408 | 409 | if (!$hasBuildInfo || !array_key_exists('ZendVersion', $storageConfig['BuildInfo'])) { 410 | $storageConfig['BuildInfo']['ZendVersion'] = get_zendversion_phpdir($storagePath); 411 | $needUpdateInfo = true; 412 | } 413 | 414 | if ($needUpdateInfo) { 415 | $this->saveStorageConfig($storagePath, $storageConfig); 416 | } 417 | 418 | $avaiableVersions[$storageConfig['BuildInfo']['Version']] = [ 419 | 'storagePath' => $storagePath, 420 | 'architecture' => $storageConfig['BuildInfo']['Architecture'], 421 | 'compiler' => $storageConfig['BuildInfo']['Compiler'], 422 | 'buildDate' => $storageConfig['BuildInfo']['BuildDate'], 423 | 'zendVersion' => $storageConfig['BuildInfo']['ZendVersion'], 424 | 'isAddOnBuild' => array_key_exists('AddOnBuild', $storageConfig['RepoImporting']) ? (bool) $storageConfig['RepoImporting']['AddOnBuild'] : false 425 | ]; 426 | } 427 | 428 | uksort($avaiableVersions, 'strnatcmp'); 429 | 430 | $this->versions = $avaiableVersions; 431 | 432 | closedir($handle); 433 | 434 | return $this; 435 | } 436 | } 437 | -------------------------------------------------------------------------------- /src/helpers.php: -------------------------------------------------------------------------------- 1 | ')) { 147 | $grepInfo = explode(' => ', $output[0])[1]; 148 | } 149 | 150 | switch ($grepInfo) { 151 | case 'x86': 152 | $architecture = 32; 153 | break; 154 | 155 | case 'x64': 156 | $architecture = 64; 157 | break; 158 | 159 | default: 160 | $architecture = exec('"' . $dirPath . DIRECTORY_SEPARATOR . 'php.exe" -n -r "echo (PHP_INT_SIZE * 8);"'); 161 | break; 162 | } 163 | 164 | return $architecture; 165 | } 166 | } 167 | 168 | if (! function_exists('get_compiler_phpdir')) { 169 | /** 170 | * Get the compiler of PHP build which is being 171 | * stored in a specific directory 172 | * 173 | * @param string $dirPath The path to directory containing PHP build 174 | * 175 | * @return string 176 | */ 177 | function get_compiler_phpdir($dirPath) { 178 | if (! maybe_phpdir($dirPath)) { 179 | return null; 180 | } 181 | 182 | $compiler = 'Unknown'; 183 | 184 | exec('"' . $dirPath . DIRECTORY_SEPARATOR . 'php.exe" -n -i | findstr /C:"Compiler"', $output); 185 | 186 | if (strpos($output[0], ' => ')) { 187 | $compiler = explode(' => ', $output[0])[1]; 188 | } 189 | 190 | return $compiler; 191 | } 192 | } 193 | 194 | if (! function_exists('get_builddate_phpdir')) { 195 | /** 196 | * Get the build date of the PHP build which is 197 | * being stored in a specific directory 198 | * 199 | * @param string $dirPath The path to directory containing PHP build 200 | * 201 | * @return string 202 | */ 203 | function get_builddate_phpdir($dirPath) { 204 | if (! maybe_phpdir($dirPath)) { 205 | return null; 206 | } 207 | 208 | $buildDate = 'Unknown'; 209 | 210 | exec('"' . $dirPath . DIRECTORY_SEPARATOR . 'php.exe" -n -i | findstr /C:"Build Date"', $output); 211 | 212 | if (strpos($output[0], ' => ')) { 213 | $buildDate = explode(' => ', $output[0])[1]; 214 | } 215 | 216 | return $buildDate; 217 | } 218 | } 219 | 220 | if (! function_exists('get_zendversion_phpdir')) { 221 | /** 222 | * Get the Zend Engine version of the PHP build 223 | * which is being stored in a specific directory 224 | * 225 | * @param string $dirPath The path to directory containing PHP build 226 | * 227 | * @return string 228 | */ 229 | function get_zendversion_phpdir($dirPath) { 230 | if (! maybe_phpdir($dirPath)) { 231 | return null; 232 | } 233 | 234 | return exec('"' . $dirPath . DIRECTORY_SEPARATOR . 'php.exe" -n -r "echo zend_version();"'); 235 | } 236 | } 237 | 238 | if (! function_exists('is_phpversion')) { 239 | /** 240 | * Determine the string is PHP version number 241 | * 242 | * @param string $string 243 | * 244 | * @return boolean 245 | */ 246 | function is_phpversion($string) { 247 | return preg_match('/^\d+\.\d+\.\d+(-extra)?$/', $string); 248 | } 249 | } 250 | 251 | if (! function_exists('get_major_phpversion')) { 252 | /** 253 | * Get major part from PHP version number 254 | * 255 | * @param string $version The PHP version number 256 | * 257 | * @return string 258 | */ 259 | function get_major_phpversion($version) { 260 | if (! is_phpversion($version)) { 261 | return null; 262 | } 263 | 264 | return explode('.', $version)[0]; 265 | } 266 | } 267 | 268 | if (! function_exists('get_minor_phpversion')) { 269 | /** 270 | * Get minor part from PHP version number 271 | * 272 | * @param string $version The PHP version number 273 | * 274 | * @return string 275 | */ 276 | function get_minor_phpversion($version) { 277 | if (! is_phpversion($version)) { 278 | return null; 279 | } 280 | 281 | return explode('.', $version)[1]; 282 | } 283 | } 284 | 285 | if (! function_exists('get_release_phpversion')) { 286 | /** 287 | * Get release part from PHP version number 288 | * 289 | * @param string $version The PHP version number 290 | * 291 | * @return string 292 | */ 293 | function get_release_phpversion($version) { 294 | if (! is_phpversion($version)) { 295 | return null; 296 | } 297 | 298 | return explode('.', $version)[2]; 299 | } 300 | } 301 | 302 | if (! function_exists('get_extra_phpversion')) { 303 | /** 304 | * Get extra part from PHP version number 305 | * 306 | * @param string $version The PHP version number 307 | * 308 | * @return string 309 | */ 310 | function get_extra_phpversion($version) { 311 | if (! is_phpversion($version)) { 312 | return null; 313 | } 314 | 315 | $splitParts = explode('-', $version); 316 | 317 | if (count($splitParts) <= 1) { 318 | return null; 319 | } 320 | 321 | return $splitParts[1]; 322 | } 323 | } 324 | 325 | if (! function_exists('maybe_path')) { 326 | /** 327 | * Determine the string can be a path 328 | * 329 | * @param string $string 330 | * 331 | * @return boolean 332 | */ 333 | function maybe_path($string) { 334 | return (bool) preg_match('/^[^\"\<\>\?\*\|]+$/', $string); 335 | } 336 | } 337 | 338 | if (! function_exists('winstyle_path')) { 339 | /** 340 | * Convert paths to Windows style 341 | * 342 | * @param string $path 343 | * 344 | * @return string 345 | */ 346 | function winstyle_path($path) { 347 | return str_replace('/', '\\', $path); 348 | } 349 | } 350 | 351 | if (! function_exists('unixstyle_path')) { 352 | /** 353 | * Convert paths to Unix style 354 | * 355 | * @param string $path 356 | * 357 | * @return string 358 | */ 359 | function unixstyle_path($path) { 360 | return str_replace('\\', '/', $path); 361 | } 362 | } 363 | 364 | if (! function_exists('absolute_path')) { 365 | /** 366 | * Return absolute path from input path. 367 | * This function is an alternative to realpath() function for non-existent paths. 368 | * 369 | * @param string $path The input path 370 | * @param string $separator The directory separator wants to use in the results 371 | * 372 | * @return string 373 | */ 374 | function absolute_path($path, $separator = DIRECTORY_SEPARATOR) { 375 | // Normalize directory separators 376 | $path = str_replace(['/', '\\'], $separator, $path); 377 | 378 | // Store root part of path 379 | $root = null; 380 | while (is_null($root)) { 381 | // Check if path start with a separator (UNIX) 382 | if (substr($path, 0, 1) === $separator) { 383 | $root = $separator; 384 | $path = substr($path, 1); 385 | break; 386 | } 387 | 388 | // Check if path start with drive letter (WINDOWS) 389 | preg_match('/^[a-z]:/i', $path, $matches); 390 | if (isset($matches[0])) { 391 | $root = $matches[0] . $separator; 392 | $path = substr($path, 2); 393 | break; 394 | } 395 | 396 | $path = getcwd() . $separator . $path; 397 | } 398 | 399 | // Get and filter empty sub paths 400 | $subPaths = array_filter(explode($separator, $path), 'strlen'); 401 | 402 | $absolutes = []; 403 | foreach ($subPaths as $subPath) { 404 | if ('.' === $subPath) { 405 | continue; 406 | } 407 | 408 | if ('..' === $subPath) { 409 | array_pop($absolutes); 410 | continue; 411 | } 412 | 413 | $absolutes[] = $subPath; 414 | } 415 | 416 | return $root . implode($separator, $absolutes); 417 | } 418 | } 419 | 420 | if (! function_exists('relative_path')) { 421 | /** 422 | * Return relative path from source directory to destination 423 | * 424 | * @param string $from The path of source directory 425 | * @param string $to The path of file or directory to be compare 426 | * @param string $separator The directory separator wants to use in the results 427 | * 428 | * @return string 429 | */ 430 | function relative_path($from, $to, $separator = DIRECTORY_SEPARATOR) { 431 | $fromParts = explode($separator, absolute_path($from, $separator)); 432 | $toParts = explode($separator, absolute_path($to, $separator)); 433 | $diffFromTo = array_diff($fromParts, $toParts); 434 | $diffToFrom = array_diff($toParts, $fromParts); 435 | 436 | if ($diffToFrom === $toParts) { 437 | return implode($separator, $toParts); 438 | } 439 | 440 | return str_repeat('..' . $separator, count($diffFromTo)) . implode($separator, $diffToFrom); 441 | } 442 | } 443 | 444 | if (! function_exists('create_ini_file')) { 445 | /** 446 | * Create ini file 447 | * 448 | * @param string $filename The path to file want to create 449 | * @param array $data The content want to save 450 | * @param boolean $process_sessions Use session names in data 451 | * 452 | * @return boolean 453 | */ 454 | function create_ini_file($filename, $data = [], $process_sessions = false) { 455 | $content = ''; 456 | 457 | if ((bool) $process_sessions) { 458 | foreach ($data as $section => $values) { 459 | $content .= PHP_EOL . '[' . $section. ']' . PHP_EOL; 460 | 461 | foreach ($values as $key => $value) { 462 | if (is_array($value)) { 463 | for ($i = 0; $i < count($value); $i++) { 464 | $content .= $key . '[] = "' . $value[$i] . '"' . PHP_EOL; 465 | } 466 | } else if (empty($value)) { 467 | $content .= $key . ' = ' . PHP_EOL; 468 | } else { 469 | $content .= $key . ' = "' . str_replace('"', '\"', $value) . '"' . PHP_EOL; 470 | } 471 | } 472 | } 473 | } else { 474 | foreach ($data as $key => $value) { 475 | if (is_array($value)) { 476 | for ($i = 0; $i < count($value); $i++) { 477 | $content .= $key . '[] = "' . $value[$i] . '"' . PHP_EOL; 478 | } 479 | } else if (empty($value)) { 480 | $content .= $key . ' = ' . PHP_EOL; 481 | } else { 482 | $content .= $key . ' = "' . str_replace('"', '\"', $value) . '"' . PHP_EOL; 483 | } 484 | } 485 | } 486 | 487 | return file_put_contents($filename, ltrim($content)); 488 | } 489 | } 490 | -------------------------------------------------------------------------------- /src/Support/Handler.php: -------------------------------------------------------------------------------- 1 | requireInstall(); 14 | } 15 | 16 | parent::__construct(); 17 | 18 | $this->currentStoragePath = readlink($this->paths['phpDir']); 19 | 20 | if ($this->currentStoragePath == $this->paths['phpDir']) { 21 | $this->requireInstall(); 22 | } 23 | 24 | $this->currentVersion = get_version_phpdir($this->paths['phpDir']); 25 | } 26 | 27 | public function showVersion($version = null) 28 | { 29 | if ($version == 'current' || (empty($version) && count($this->repository->versions) == 1)) { 30 | $version = $this->currentVersion; 31 | $message = 'The current PHP build has the following information:'; 32 | } else { 33 | $version = $this->getVersionOrList($version, 'Choose one of the following builds to show details:'); 34 | $message = 'The PHP build you require has the following information:'; 35 | } 36 | 37 | if (! array_key_exists($version, $this->repository->versions)) { 38 | Console::terminate('Sorry! Do not found any PHP version that you entered.', 1); 39 | } 40 | 41 | $architecture = $this->repository->versions[$version]['architecture']; 42 | $isBuiltInVersion = $this->isBuiltInVersion($version); 43 | 44 | Console::line($message); 45 | Console::breakline(); 46 | Console::line('Version : ' . $version . ' ('. (($isBuiltInVersion) ? 'Built-in' : 'Add-on') . ' version)'); 47 | Console::line('Storage path : ' . $this->repository->versions[$version]['storagePath']); 48 | Console::line('Compiler : ' . $this->repository->versions[$version]['compiler']); 49 | Console::line('Architecture : ' . $architecture . ' bit (' . (($architecture == '32') ? 'x86' : 'x64') . ')'); 50 | Console::line('Build date : ' . $this->repository->versions[$version]['buildDate']); 51 | Console::line('Zend version : ' . $this->repository->versions[$version]['zendVersion']); 52 | 53 | Console::breakline(); 54 | Console::hrline(); 55 | Console::terminate('Your request is completed.'); 56 | } 57 | 58 | public function listVersions() 59 | { 60 | $totalVersions = count($this->repository->versions); 61 | 62 | if ($totalVersions == 1) { 63 | Console::line('There is only one PHP build in the repository as follows:'); 64 | } else { 65 | Console::line('There are ' . $totalVersions . ' PHP builds in repository as follows:'); 66 | } 67 | 68 | Console::breakline(); 69 | 70 | $maxLenVersion = max(array_map(function($item) { 71 | return strlen($item); 72 | }, array_keys($this->repository->versions))); 73 | 74 | $maxLenStoragePath = max(array_map(function($item) { 75 | return strlen($item['storagePath']); 76 | }, $this->repository->versions)); 77 | 78 | $count = 0; 79 | 80 | foreach ($this->repository->versions as $version => $info) { 81 | $isBuiltInVersion = $this->isBuiltInVersion($version); 82 | 83 | $col_1_content = str_pad(++$count, strlen($totalVersions), ' ', STR_PAD_LEFT) . '. '; 84 | $col_2_content = 'Version ' . str_pad($version, $maxLenVersion + 2); 85 | $col_3_content = '- Stored at: ' . str_pad($info['storagePath'], $maxLenStoragePath + 2); 86 | 87 | Console::line($col_1_content, false); 88 | Console::line($col_2_content, false); 89 | Console::line($col_3_content, false); 90 | 91 | if ($isBuiltInVersion) { 92 | Console::line('- Built-in', false); 93 | } else { 94 | Console::line('- Add-on', false); 95 | } 96 | 97 | if ($this->isCurrentVersion($version)) { 98 | Console::line(', in use'); 99 | } else { 100 | Console::breakline(); 101 | } 102 | } 103 | 104 | Console::breakline(); 105 | Console::hrline(); 106 | Console::terminate('Your request is completed.'); 107 | } 108 | 109 | public function addVersion($source = null) 110 | { 111 | // Compatibility verification 112 | if (! $source) { 113 | Console::line('Please provide the path to new Xampp PHP directory you want to add.'); 114 | $source = Console::ask('Enter the path'); 115 | Console::breakline(); 116 | } 117 | 118 | if (! maybe_phpdir($source)) { 119 | Console::line('The directory you provided is not PHP directory.'); 120 | Console::line('Cancel the adding process.'); 121 | Console::breakline(); 122 | Console::terminate(null, 1); 123 | } 124 | 125 | $architecture = get_architecture_phpdir($source); 126 | 127 | if ($architecture != $this->repository->architecture) { 128 | Console::line('The directory that you provided is not compatible with your Xampp.'); 129 | Console::line('Cancel the adding process.'); 130 | Console::breakline(); 131 | Console::terminate(null, 1); 132 | } 133 | 134 | $version = get_version_phpdir($source); 135 | 136 | if (array_key_exists($version, $this->repository->versions)) { 137 | Console::line('The directory you provided contains the PHP version you already have.'); 138 | Console::line('No need to add this version.'); 139 | Console::breakline(); 140 | Console::terminate(); 141 | } 142 | 143 | $needBuildConfig = false; 144 | $phpMajorVersion = get_major_phpversion($version); 145 | $configFile = str_replace('{{php_major_version}}', $phpMajorVersion, $this->paths['httpdXamppPHP']); 146 | $configTemplateFile = str_replace('{{php_major_version}}', $phpMajorVersion, $this->paths['httpdXamppTemplate']); 147 | $configSourceFile = realpath($source . '\..\apache\conf\extra\httpd-xampp.conf'); 148 | 149 | if (! is_file($configFile)) { 150 | $needBuildConfig = true; 151 | 152 | if (is_file($configTemplateFile)) { 153 | $configContent = @file_get_contents($configTemplateFile); 154 | $configContent = str_replace('{{xamppDir}}', unixstyle_path($this->paths['xamppDir']), $configContent); 155 | } elseif (is_file($configSourceFile)) { 156 | $configContent = @file_get_contents($configSourceFile); 157 | $configContent = preg_replace('/(\"|\')\/xampp/', '${1}' . unixstyle_path($this->paths['xamppDir']), $configContent); 158 | } else { 159 | Console::line('This PHP version is not supported yet.'); 160 | Console::line('Cancel the adding process.'); 161 | Console::breakline(); 162 | Console::terminate(); 163 | } 164 | } 165 | 166 | // Adding confirmation 167 | $compiler = get_compiler_phpdir($source); 168 | $buildDate = get_builddate_phpdir($source); 169 | $zendVersion = get_zendversion_phpdir($source); 170 | 171 | Console::line('The PHP build you provided has the following information:'); 172 | Console::breakline(); 173 | Console::line('Version : ' . $version); 174 | Console::line('Compiler : ' . $compiler); 175 | Console::line('Architecture : ' . $architecture . ' bit (' . (($architecture == '32') ? 'x86' : 'x64') . ')'); 176 | Console::line('Build date : ' . $buildDate); 177 | Console::line('Zend version : ' . $zendVersion); 178 | Console::breakline(); 179 | 180 | if (! Console::confirm('Do you want to continue the adding process?')) { 181 | Console::breakline(); 182 | Console::terminate('The action is canceled by user.'); 183 | } 184 | 185 | Console::breakline(); 186 | Console::hrline(); 187 | Console::line('Start adding new PHP build.'); 188 | Console::breakline(); 189 | 190 | // Copy into repository 191 | $message = 'Copying directory of new PHP build into the repository...'; 192 | Console::line($message, false); 193 | 194 | $importResult = $this->repository->importBuild($source, [ 195 | 'version' => $version, 196 | 'architecture' => $architecture, 197 | 'compiler' => $compiler, 198 | 'buildDate' => $buildDate, 199 | 'zendVersion' => $zendVersion 200 | ], false); 201 | 202 | if ($importResult['error_code'] != 0) { 203 | Console::line('Failed', true, max(77 - strlen($message), 1)); 204 | Console::breakline(); 205 | Console::terminate(null, 1); 206 | } 207 | 208 | Console::line('Successful', true, max(73 - strlen($message), 1)); 209 | 210 | // Standardize paths 211 | $message = 'Standardize paths in new PHP build to work correctly with your Xampp...'; 212 | Console::line($message, false); 213 | 214 | $storagePath = $importResult['data']['storagePath']; 215 | $storageConfig = $importResult['data']['storageConfig']; 216 | 217 | if ($storageConfig['RepoImporting']['PathStandardized']) { 218 | $standardizePattern = '/' . preg_quote($storageConfig['RepoImporting']['PathStandardized'], '/') . '/i'; 219 | $standardizeReplacement = $this->paths['xamppDir']; 220 | } else { 221 | $standardizePattern = '/([\!\=\'\"\n]{1}[\s]?)(\w\:)?\\\\xampp/i'; 222 | $standardizeReplacement = '${1}' . $this->paths['xamppDir']; 223 | } 224 | 225 | $standardizeActions = [ 226 | [ 227 | 'type' => 'replace', 228 | 'pattern' => $standardizePattern, 229 | 'replacement' => $standardizeReplacement, 230 | 'files' => $this->prepareStandardizeList() 231 | ], 232 | [ 233 | 'type' => 'replace', 234 | 'pattern' => '/' . preg_quote($this->paths['xamppDir'] . '\php/Text/Highlighter/generate.bat', '/') . '/i', 235 | 'replacement' => '"' . $this->paths['xamppDir'] . '\php\Text\Highlighter\generate.bat"', 236 | 'files' => ['Text\Highlighter\generate.bat'] 237 | ] 238 | ]; 239 | 240 | if (! $this->repository->editBuild($version, $standardizeActions, false)) { 241 | Console::line('Failed', true, max(77 - strlen($message), 1)); 242 | Console::breakline(); 243 | Console::line('Removing recently added PHP build...'); 244 | $this->repository->removeBuild($version, false); 245 | Console::terminate(null, 1); 246 | } 247 | 248 | $storageConfig['RepoImporting']['PathStandardized'] = $this->paths['xamppDir']; 249 | $this->repository->saveStorageConfig($storagePath, $storageConfig); 250 | Console::line('Successful', true, max(73 - strlen($message), 1)); 251 | 252 | // Create httpd-xamm-php{{php_major_version}}.conf 253 | if ($needBuildConfig) { 254 | $message = 'Creating the "httpd-xampp.conf" file specific to the new PHP build...'; 255 | Console::line($message, false); 256 | 257 | @file_put_contents($configFile, $configContent); 258 | 259 | Console::line('Successful', true, max(73 - strlen($message), 1)); 260 | } 261 | 262 | // Notify adding result 263 | Console::breakline(); 264 | Console::line('New PHP build has been added to the repository at: ' . $storagePath); 265 | 266 | // Ask to add more 267 | Console::breakline(); 268 | Console::hrline(); 269 | 270 | $addMore = Console::confirm('Do you want to add another PHP build?'); 271 | 272 | if ($addMore) { 273 | Console::breakline(); 274 | $this->addVersion(); 275 | } 276 | 277 | // Exit 278 | Console::breakline(); 279 | Console::terminate('All jobs are completed.'); 280 | } 281 | 282 | public function removeVersion($version = null) 283 | { 284 | $version = $this->getVersionOrList($version, 'You can choose one of the following builds to remove:', false); 285 | 286 | if (! array_key_exists($version, $this->repository->versions)) { 287 | Console::terminate('Sorry! Do not found any PHP version that you entered.', 1); 288 | } 289 | 290 | if ($this->isCurrentVersion($version)) { 291 | Console::terminate('You are running on this PHP version, so you cannot remove it.', 1); 292 | } 293 | 294 | if ($this->isBuiltInVersion($version)) { 295 | Console::terminate('This is the original version of Xampp, does not allow removal.', 1); 296 | } 297 | 298 | $confirmRemove = Console::confirm('Are you sure you want to remove PHP version "' . $version . '" ?'); 299 | Console::breakline(); 300 | 301 | if (! $confirmRemove) { 302 | Console::terminate('Cancel the action.'); 303 | } 304 | 305 | Console::hrline(); 306 | Console::line('Start removing PHP version "' . $version . '" from repository.'); 307 | Console::breakline(); 308 | 309 | // Delete PHPBIN file 310 | $message = 'Deleting main PHP binary file of the build...'; 311 | Console::line($message, false); 312 | 313 | $storagePath = $this->repository->versions[$version]['storagePath']; 314 | $removePHPBin = @unlink($storagePath . '\php.exe'); 315 | 316 | if (! $removePHPBin) { 317 | Console::line('Failed', true, max(77 - strlen($message), 1)); 318 | Console::breakline(); 319 | Console::terminate(null, 1); 320 | } 321 | 322 | Console::line('Successful', true, max(73 - strlen($message), 1)); 323 | 324 | // Delete folder 325 | $message = 'Deleting the directory of the build...'; 326 | Console::line($message, false); 327 | 328 | $removeDir = $this->repository->removeBuild($version, false); 329 | 330 | if (! $removeDir) { 331 | Console::line('Failed', true, max(77 - strlen($message), 1)); 332 | Console::breakline(); 333 | Console::line('You must delete directory "' . $storagePath . '" manually.'); 334 | } else { 335 | Console::line('Successful', true, max(73 - strlen($message), 1)); 336 | } 337 | 338 | // Ask to remove more 339 | Console::breakline(); 340 | Console::hrline(); 341 | 342 | $removeMore = Console::confirm('Do you want to remove another PHP version'); 343 | 344 | if ($removeMore) { 345 | Console::breakline(); 346 | $this->removeVersion(); 347 | } 348 | 349 | // Exit 350 | Console::breakline(); 351 | Console::terminate('All jobs are completed.'); 352 | } 353 | 354 | public function switchVersion($version = null) 355 | { 356 | $version = $this->getVersionOrList($version, 'You can choose one of the following builds to switch to:', false); 357 | 358 | if (! array_key_exists($version, $this->repository->versions)) { 359 | Console::terminate('Sorry! Do not found any PHP version that you entered.', 1); 360 | } 361 | 362 | if ($this->isCurrentVersion($version)) { 363 | Console::terminate('You are running PHP ' . $this->currentVersion . ', so you don\'t need to do anymore.'); 364 | } 365 | 366 | $switchConfirm = Console::confirm('Are you sure you want to switch to PHP version "' . $version . '" ?'); 367 | Console::breakline(); 368 | 369 | if (! $switchConfirm) { 370 | Console::terminate('Cancel the action.'); 371 | } 372 | 373 | Console::hrline(); 374 | Console::line('Start switching to PHP version ' . $version); 375 | Console::breakline(); 376 | 377 | // Stop Apache if necessary 378 | $apacheRunning = $this->isApacheRunning(); 379 | if ($apacheRunning) { 380 | $this->stopApache(false); 381 | } 382 | 383 | // Create symbolic link 384 | $message = 'Creating symbolic link to corresponding PHP build in repository...'; 385 | Console::line($message, false); 386 | 387 | $resultMap = $this->repository->mapToUse($version, $this->paths['phpDir']); 388 | 389 | if (! $resultMap) { 390 | Console::line('Failed', true, max(77 - strlen($message), 1)); 391 | Console::breakline(); 392 | Console::terminate('The switching process has failed.', 1); 393 | } 394 | 395 | Console::line('Successful', true, max(73 - strlen($message), 1)); 396 | 397 | // Update httpd-xampp.conf 398 | $message = 'Updating the "httpd-xampp.conf" file to corresponding PHP build...'; 399 | Console::line($message, false); 400 | 401 | $httpdXamppPHP = str_replace('{{php_major_version}}', get_major_phpversion($version), $this->paths['httpdXamppPHP']); 402 | $fileUpdated = @file_put_contents($this->paths['httpdXampp'], 'Include "' . relative_path($this->paths['apacheDir'], $httpdXamppPHP, '/') . '"' . PHP_EOL); 403 | 404 | if (! $fileUpdated) { 405 | Console::line('Failed', true, max(77 - strlen($message), 1)); 406 | Console::breakline(); 407 | Console::terminate('The switching process has failed.', 1); 408 | } 409 | 410 | Console::line('Successful', true, max(73 - strlen($message), 1)); 411 | 412 | // Restart Apache if necessary 413 | if ($apacheRunning) { 414 | $this->startApache(false); 415 | } 416 | 417 | // Update current version info 418 | $this->currentVersion = $version; 419 | $this->currentStoragePath = $this->repository->versions[$version]['storagePath']; 420 | 421 | // Show result of task 422 | Console::breakline(); 423 | Console::line('The version switching is completed.'); 424 | Console::line('You are running PHP ' . $version); 425 | 426 | Console::breakline(); 427 | Console::hrline(); 428 | Console::terminate('Your request is completed.'); 429 | } 430 | 431 | private function requireInstall() 432 | { 433 | Console::line('Xampp PHP Switcher has not been integrated into Xampp.'); 434 | Console::line('Run command "xphp install" in Administartor mode to integrate it.'); 435 | Console::terminate(null, 1); 436 | } 437 | 438 | private function getVersionOrList($version = null, $message = null, $includeCurrent = true) 439 | { 440 | if (is_phpversion($version)) { 441 | return $version; 442 | } 443 | 444 | $message = $message ?: 'Please choose one of the following versions:'; 445 | 446 | if (! $includeCurrent) { 447 | Console::line('You are running PHP ' . $this->currentVersion, false); 448 | 449 | if ($this->isBuiltInVersion($this->currentVersion)) { 450 | Console::line(' (Built-in version)', false); 451 | } else { 452 | Console::line(' (Add-on version)', false); 453 | } 454 | 455 | Console::line(', stored at ' . $this->currentStoragePath); 456 | Console::breakline(); 457 | } 458 | 459 | $options = $this->makeVersionList($includeCurrent); 460 | $totalOption = count($options); 461 | 462 | if ($totalOption == 0) { 463 | Console::terminate('There are no other PHP builds to use.'); 464 | } 465 | 466 | Console::line($message); 467 | Console::breakline(); 468 | 469 | $maxLenVersion = max(array_map(function($item) { 470 | return strlen($item); 471 | }, array_keys($this->repository->versions))); 472 | 473 | $maxLenStoragePath = max(array_map(function($item) { 474 | return strlen($item['storagePath']); 475 | }, $this->repository->versions)); 476 | 477 | foreach ($options as $optionId => $version) { 478 | $storagePath = $this->repository->versions[$version]['storagePath']; 479 | 480 | $col_1_content = str_pad('[' . $optionId . ']', strlen($totalOption) + 2, ' ', STR_PAD_LEFT) . ' '; 481 | $col_2_content = 'Version ' . str_pad($version, $maxLenVersion + 2); 482 | $col_3_content = '- Stored at: ' . str_pad($storagePath, $maxLenStoragePath + 2); 483 | 484 | Console::line($col_1_content, false); 485 | Console::line($col_2_content, false); 486 | Console::line($col_3_content, false); 487 | 488 | if ($this->isBuiltInVersion($version)) { 489 | Console::line('- Built-in', false); 490 | } else { 491 | Console::line('- Add-on', false); 492 | } 493 | 494 | if ($this->isCurrentVersion($version)) { 495 | Console::line(', in use'); 496 | } else { 497 | Console::breakline(); 498 | } 499 | } 500 | 501 | Console::breakline(); 502 | 503 | $selection = -1; 504 | $repeat = 0; 505 | 506 | while ($selection <= 0 || $selection > $totalOption) { 507 | if ($repeat == 4) { 508 | Console::terminate('You have entered an incorrect format many times.', 1); 509 | } 510 | 511 | if ($repeat == 0) { 512 | $selection = Console::ask('Please pick an option (type ordinal number, or leave it blank to exit)'); 513 | } else { 514 | Console::line('You have entered an incorrect format.'); 515 | $selection = Console::ask('Please pick an option again (type ordinal number, or leave it blank to exit)'); 516 | } 517 | 518 | Console::breakline(); 519 | 520 | if (is_null($selection)) { 521 | Console::line('Xampp PHP Switcher is terminating on demand...'); 522 | Console::terminate(null, 0, true); 523 | } 524 | 525 | $selection = ((int) $selection); 526 | $repeat++; 527 | } 528 | 529 | return $options[$selection]; 530 | } 531 | 532 | private function makeVersionList($includeCurrent = true) 533 | { 534 | $options = []; 535 | $startNum = 1; 536 | 537 | foreach ($this->repository->versions as $version => $info) { 538 | if ($includeCurrent) { 539 | $options[$startNum++] = $version; 540 | } else { 541 | if (! $this->isCurrentVersion($version)) { 542 | $options[$startNum++] = $version; 543 | } 544 | } 545 | } 546 | 547 | return $options; 548 | } 549 | 550 | private function isCurrentVersion($version) 551 | { 552 | return $this->currentVersion === $version; 553 | } 554 | 555 | private function isBuiltInVersion($version) 556 | { 557 | return (! $this->repository->versions[$version]['isAddOnBuild']); 558 | } 559 | 560 | private function prepareStandardizeList() 561 | { 562 | $list = []; 563 | $listFile = $this->paths['srcDir'] . '\need_standardize.lst'; 564 | 565 | if (is_file($listFile)) { 566 | $autodetect = ini_get('auto_detect_line_endings'); 567 | 568 | ini_set('auto_detect_line_endings', '1'); 569 | 570 | $list = file($listFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); 571 | 572 | ini_set('auto_detect_line_endings', $autodetect); 573 | } 574 | 575 | return $list; 576 | } 577 | } 578 | --------------------------------------------------------------------------------