├── .gitignore ├── README.md ├── package-lock.json ├── package.json └── src ├── fpm.js ├── index.js └── php.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP Version Manager 2 | 3 | NPM 4 | NPM 5 | NPM 6 | 7 | ## Installation 8 | 9 | ```bash 10 | npm install --global php-version-manager 11 | ``` 12 | 13 | ## Usage 14 | 15 | ```bash 16 | pvm --help 17 | 18 | # output 19 | Usage: pvm [options] [command] 20 | 21 | PHP Version Manager version x.x.x 22 | 23 | Options: 24 | -v, --version Output the current application version 25 | -h, --help output usage information 26 | 27 | Commands: 28 | status|s Show current PHP version status 29 | ls List available PHP versions 30 | use|u Switch PHP version 31 | xdebug|x [sapi] [status] Manage XDebug status 32 | restart|r Restart PHP-FPM and NGINX 33 | 34 | ``` 35 | 36 | ### Status 37 | 38 | Show current PHP version and XDebug status 39 | 40 | ```bash 41 | pvm status 42 | 43 | # output 44 | PHP Version Manager version x.x.x 45 | 46 | PHP: 7.3 CLI: OFF FPM: OFF 47 | ``` 48 | 49 | ### List versions 50 | 51 | List installed PHP versions 52 | 53 | ```bash 54 | pvm ls 55 | 56 | # output 57 | 5.6 58 | 7.0 59 | 7.1 60 | 7.2 61 | 7.3 62 | ``` 63 | 64 | ### Switch PHP version 65 | 66 | ``` 67 | pvm use 7.2 68 | ``` 69 | 70 | ### Manage XDebug 71 | 72 | ```bash 73 | # Toggle XDebug for cli and fpm 74 | pvm xdebug 75 | 76 | # Enable XDebug for cli and fpm 77 | pvm xdebug on 78 | 79 | # Disable XDebug for cli and fpm 80 | pvm xdebug off 81 | 82 | # Enable XDebug for cli only 83 | pvm xdebug cli on 84 | 85 | # Disable XDebug for fpm only 86 | pvm xdebug fpm off 87 | 88 | # Toggle XDebug for cli 89 | pvm xdebug cli 90 | 91 | # Toggle XDebug for fpm 92 | pvm xdebug fpm 93 | ``` 94 | 95 | ### Restart Nginx and PHP-FPM 96 | 97 | ```bash 98 | pvm restart 99 | ``` 100 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "php-version-manager", 3 | "version": "0.3.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ansi-styles": { 8 | "version": "3.2.1", 9 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 10 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 11 | "requires": { 12 | "color-convert": "^1.9.0" 13 | } 14 | }, 15 | "chalk": { 16 | "version": "2.4.2", 17 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 18 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 19 | "requires": { 20 | "ansi-styles": "^3.2.1", 21 | "escape-string-regexp": "^1.0.5", 22 | "supports-color": "^5.3.0" 23 | } 24 | }, 25 | "color-convert": { 26 | "version": "1.9.3", 27 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 28 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 29 | "requires": { 30 | "color-name": "1.1.3" 31 | } 32 | }, 33 | "color-name": { 34 | "version": "1.1.3", 35 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 36 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 37 | }, 38 | "commander": { 39 | "version": "3.0.2", 40 | "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz", 41 | "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==" 42 | }, 43 | "escape-string-regexp": { 44 | "version": "1.0.5", 45 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 46 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 47 | }, 48 | "has-flag": { 49 | "version": "3.0.0", 50 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 51 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 52 | }, 53 | "prettier": { 54 | "version": "1.18.2", 55 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.18.2.tgz", 56 | "integrity": "sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==", 57 | "dev": true 58 | }, 59 | "supports-color": { 60 | "version": "5.5.0", 61 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 62 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 63 | "requires": { 64 | "has-flag": "^3.0.0" 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "php-version-manager", 3 | "version": "0.3.1", 4 | "description": "Linux command line tool for managing locally installed PHP versions.", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"no test specified\" && exit 0" 8 | }, 9 | "keywords": [ 10 | "cli", 11 | "php", 12 | "pvm" 13 | ], 14 | "author": "Chris ", 15 | "license": "MIT", 16 | "dependencies": { 17 | "chalk": "^2.4.2", 18 | "commander": "^3.0.2" 19 | }, 20 | "devDependencies": { 21 | "prettier": "1.18.2" 22 | }, 23 | "bin": { 24 | "pvm": "./src/index.js" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/fpm.js: -------------------------------------------------------------------------------- 1 | const { execSync } = require("child_process"); 2 | const { versions } = require("./php"); 3 | 4 | /** 5 | * Restart PHP-FPM and NGINX services 6 | * 7 | * @return {boolean} 8 | */ 9 | const restart = () => { 10 | versions().forEach(version => { 11 | execSync(`sudo /usr/sbin/service php${version}-fpm restart`); 12 | }); 13 | execSync("sudo /usr/sbin/service nginx restart"); 14 | return true; 15 | }; 16 | 17 | module.exports = { 18 | restart 19 | }; 20 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const commander = require("commander"); 4 | const chalk = require("chalk"); 5 | const php = require("./php"); 6 | const fpm = require("./fpm"); 7 | const applicationVersion = require("../package.json").version; 8 | 9 | if (process.argv.length === 2) { 10 | process.argv.push("status"); 11 | } 12 | 13 | const renderStatus = () => { 14 | console.log( 15 | chalk`\n {green PHP Version Manager} version {yellow ${applicationVersion}}\n` 16 | ); 17 | 18 | const version = php.current(); 19 | const cli = php.moduleStatus(version, "cli", "xdebug"); 20 | const fpm = php.moduleStatus(version, "fpm", "xdebug"); 21 | 22 | const phpText = "PHP: " + chalk.blue.bold(version); 23 | const cliText = 24 | "CLI: " + (cli ? chalk.green.bold("ON") : chalk.red.bold("OFF")); 25 | const fpmText = 26 | "FPM: " + (fpm ? chalk.green.bold("ON") : chalk.red.bold("OFF")); 27 | 28 | console.log(" " + [phpText, cliText, fpmText].join(" ") + "\n"); 29 | }; 30 | 31 | const program = new commander.Command(); 32 | 33 | program 34 | .name("pvm") 35 | .version( 36 | applicationVersion, 37 | "-v, --version", 38 | "output the current application version" 39 | ) 40 | .usage("[command] [options]") 41 | .description( 42 | chalk`{green PHP Version Manager} version {yellow ${applicationVersion}}` 43 | ); 44 | 45 | program 46 | .command("status") 47 | .alias("s") 48 | .description("Show current PHP version status") 49 | .action(() => { 50 | renderStatus(); 51 | 52 | process.exit(0); 53 | }); 54 | 55 | program 56 | .command("ls") 57 | .description("List available PHP versions") 58 | .action(() => { 59 | const currentVersion = php.current(); 60 | php.versions().forEach(version => { 61 | if (version === currentVersion) { 62 | console.log(chalk.green(version)); 63 | } else { 64 | console.log(version); 65 | } 66 | }); 67 | 68 | process.exit(0); 69 | }); 70 | 71 | program 72 | .command("use ") 73 | .alias("u") 74 | .description("Switch PHP version") 75 | .action(version => { 76 | if (/^\d\d$/.test(version)) { 77 | version = version.slice(0, 1) + "." + version.slice(1); 78 | } 79 | 80 | if (php.use(version)) { 81 | console.log("Restarting PHP-FPM and NGINX"); 82 | fpm.restart(); 83 | } 84 | 85 | renderStatus(); 86 | }); 87 | 88 | program 89 | .command("xdebug [sapi] [status]") 90 | .alias("x") 91 | .description("Manage XDebug status") 92 | .action((sapi, status) => { 93 | const enableOptions = ["1", "on", "yes", "y"]; 94 | const disableOptions = ["0", "off", "no", "n"]; 95 | 96 | if ( 97 | status === undefined && 98 | (enableOptions.includes(sapi) || disableOptions.includes(sapi)) 99 | ) { 100 | status = sapi; 101 | sapi = undefined; 102 | } 103 | 104 | if (sapi !== undefined && !["fpm", "cli"].includes(sapi)) { 105 | throw new Error(`Invalid SAPI \'${sapi}\', specify \'fpm\' or \'cli\'`); 106 | } 107 | 108 | if (status === undefined) { 109 | php.moduleToggle("xdebug", sapi); 110 | } else if (enableOptions.includes(status)) { 111 | php.moduleEnable("xdebug", sapi); 112 | } else if (disableOptions.includes(status)) { 113 | php.moduleDisable("xdebug", sapi); 114 | } else { 115 | throw new Error(`Invalid status \'${status}\'`); 116 | } 117 | 118 | switch (status) { 119 | case undefined: 120 | break; 121 | case "1": 122 | case "on": 123 | case "yes": 124 | case "y": 125 | php.moduleEnable("xdebug", sapi); 126 | break; 127 | case "0": 128 | case "off": 129 | case "no": 130 | case "n": 131 | php.moduleDisable("xdebug", sapi); 132 | break; 133 | default: 134 | throw new Error(`Invalid status \'${status}\'`); 135 | } 136 | 137 | if (sapi === "fpm" || sapi === undefined) { 138 | console.log("Restarting PHP-FPM and NGINX"); 139 | fpm.restart(); 140 | } 141 | 142 | renderStatus(); 143 | 144 | process.exit(0); 145 | }); 146 | 147 | program 148 | .command("restart") 149 | .alias("r") 150 | .description("Restart PHP-FPM and NGINX") 151 | .action(() => { 152 | console.log("Restarting PHP-FPM and NGINX"); 153 | fpm.restart(); 154 | process.exit(0); 155 | }); 156 | 157 | program.parse(process.argv); 158 | 159 | if (!program.args.length) renderStatus(); 160 | -------------------------------------------------------------------------------- /src/php.js: -------------------------------------------------------------------------------- 1 | const { execSync } = require("child_process"); 2 | 3 | /** 4 | * Get currently active PHP version number 5 | * 6 | * @return {string} 7 | */ 8 | const current = () => 9 | execSync('php -v | head -n 1 | cut -d " " -f 2 | cut -f1-2 -d"."') 10 | .toString() 11 | .trim(); 12 | 13 | /** 14 | * Get all installed PHP version numbers 15 | * 16 | * @return {Array.string} 17 | */ 18 | const versions = () => 19 | execSync("find /usr/bin -name 'php*.*' -type f | cut -b 13- | sort -g") 20 | .toString() 21 | .trim() 22 | .split("\n"); 23 | 24 | /** 25 | * Switch default PHP Version 26 | * 27 | * @param {string} version PHP Version number 28 | * 29 | * @return {bool|string} 30 | */ 31 | const use = version => { 32 | if (!/^\d\.\d$/.test(version) || !versions().includes(version)) { 33 | throw new Error(`Invalid version number "${version}"`); 34 | } 35 | 36 | if (version === current()) { 37 | return false; 38 | } 39 | 40 | execSync( 41 | `sudo /usr/bin/update-alternatives --set php /usr/bin/php${version}` 42 | ); 43 | 44 | return current(); 45 | }; 46 | 47 | /** 48 | * Get the status of a PHP module 49 | * 50 | * @param {string} version PHP Version 51 | * @param {string} sapi SAPI name (cli or fpm) 52 | * @param {string} module PHP Module name 53 | * 54 | * @return {boolean} 55 | */ 56 | const moduleStatus = (version, sapi, module) => { 57 | try { 58 | execSync(`phpquery -v ${version} -s ${sapi} -m ${module}`); 59 | return true; 60 | } catch (error) { 61 | return false; 62 | } 63 | }; 64 | 65 | /** 66 | * Enable PHP Module 67 | * 68 | * @param {string} module 69 | * @param {string|undefined} sapi 70 | */ 71 | const moduleEnable = (module, sapi) => { 72 | sapi = sapi ? `-s ${sapi}` : ""; 73 | execSync(`sudo /usr/sbin/phpenmod ${sapi} ${module}`); 74 | }; 75 | 76 | /** 77 | * Disable PHP Module 78 | * 79 | * @param {string} module 80 | * @param {string|undefined} sapi 81 | */ 82 | const moduleDisable = (module, sapi) => { 83 | sapi = sapi ? `-s ${sapi}` : ""; 84 | execSync(`sudo /usr/sbin/phpdismod ${sapi} ${module}`); 85 | }; 86 | 87 | const moduleToggle = (module, sapi) => { 88 | const currentVersion = current(); 89 | 90 | let cliStatus; 91 | let fpmStatus; 92 | 93 | if (sapi === "cli" || sapi === undefined) { 94 | cliStatus = moduleStatus(currentVersion, "cli", module); 95 | } 96 | 97 | if (sapi === "fpm" || sapi === undefined) { 98 | fpmStatus = moduleStatus(currentVersion, "fpm", module); 99 | } 100 | 101 | if (cliStatus === true || fpmStatus === true) { 102 | moduleDisable(module, sapi); 103 | return false; 104 | } else { 105 | moduleEnable(module, sapi); 106 | return true; 107 | } 108 | }; 109 | 110 | module.exports = { 111 | current, 112 | versions, 113 | use, 114 | moduleStatus, 115 | moduleEnable, 116 | moduleDisable, 117 | moduleToggle 118 | }; 119 | --------------------------------------------------------------------------------