├── .gitignore ├── .travis.yml ├── .travis ├── deploy_public.key └── secrets.tar.enc ├── LICENSE ├── README.md ├── bin └── deploy.sh ├── box.json ├── box.signed.json ├── composer.json ├── phpunit.hhvm.xml ├── phpunit.php ├── phpunit.xml ├── src ├── Command │ ├── Command.php │ ├── InfoCommand.php │ ├── InstallCommand.php │ ├── UninstallCommand.php │ └── UpdateCommand.php ├── Console │ └── Application.php ├── EntryPoint │ ├── bootstrap.php │ └── main.php ├── IO │ ├── ConsoleIO.php │ ├── IOInterface.php │ └── NullIO.php ├── Installer │ ├── Installer.php │ ├── LinuxInstaller.php │ ├── MacInstaller.php │ ├── UnixInstaller.php │ └── WindowsInstaller.php ├── System │ ├── BsdSystem.php │ ├── LinuxSystem.php │ ├── MacSystem.php │ ├── System.php │ ├── UnixSystem.php │ └── WindowsSystem.php └── Util │ └── ErrorHandler.php └── tests └── .gitkeep /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .travis/phar_private.key 3 | .travis/deploy_private.key 4 | .travis/secrets.tar 5 | vendor/ 6 | bin/box.phar 7 | composer.lock 8 | jupyter-php-installer.phar 9 | jupyter-php-installer.phar.pubkey -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: php 4 | 5 | cache: 6 | directories: 7 | - "$HOME/.composer/cache" 8 | - vendor 9 | 10 | matrix: 11 | fast_finish: true 12 | include: 13 | - php: 7.0 14 | env: 15 | - EXECUTE_DEPLOYMENT=true 16 | - php: 7.1 17 | 18 | before_install: 19 | - if [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then phpenv config-rm xdebug.ini ; fi 20 | - travis_retry composer self-update 21 | 22 | install: 23 | - travis_retry composer install --prefer-source --no-interaction 24 | - composer info -i 25 | 26 | script: 27 | - vendor/bin/phpunit 28 | 29 | after_success: 30 | - if [[ $EXECUTE_DEPLOYMENT == 'true' && $TRAVIS_PULL_REQUEST == 'false' ]]; then composer install --no-dev --prefer-dist --no-interaction ; fi 31 | - if [[ $EXECUTE_DEPLOYMENT == 'true' && $TRAVIS_PULL_REQUEST == 'false' ]]; then ./bin/deploy.sh ; fi 32 | -------------------------------------------------------------------------------- /.travis/deploy_public.key: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDUXU2ODDrbfdRatnMFgSzEJHvThGeNbvgo6WYKmy35ER+CctnVrC/SQ1tcXAHC+F6GKhSoJDMVazti3i9wp69bkYmGTY/OKbYltBP5wldtD4oF1mTmWzAB6EoiPac9BNddry57sHFH7N/zlQVSf9ZuMd3iXJLTTD6VJeVWuSTiIW/efby9ICVyoGEqnNBeoxDFD3L9w6tW9IPZHh5Sma2dzr3+C4TsEGFmDFYIEtK+Ar49XH03OH7hd5xALqxxSTE5c40Dt5N106ch6eKb2BhA9fVIFbS9/hhgh+ZV5n1Y53op0CDK8aadoQTzvQK/AdfSnDoX2hQWNBPAzyTJps6q2OoZdaV+gbAahsHwOgvRn4kYChUzU201DcRi4xMI2QkQ4f+NPUTgtJnvRbZUPRO7va7PLkXEEZqFv1Xs6sE1ua7fB7NUyLb9MrQznJrX7fOHE82veoDrqn2du3cEPJ/FATqL+dzynB6YjOk7J1F7qaxSAaURM3fG12dcbZYgfVKTDo+OieLEoYqtm8FGMTPM8xIQR8wm4euEDxsh6Ex4sDMHKXIRu88Hx9VPwYUzv1vIVQfbbKIc65G91zJm91cW5t0dsKY9QD2u2PzDY9EmwNfihRY+jMP2A47OSzNCaqdx/qj7SG7GUjnlvoi1/sKhGP5gNwVtuqSnVlogN0MXyQ== castarco@litipk.com 2 | -------------------------------------------------------------------------------- /.travis/secrets.tar.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coder-Spirit/Jupyter-PHP-Installer/ac86a042b6db8fd196062775836831bfe675b0a7/.travis/secrets.tar.enc -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2017 Litipk 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jupyter-PHP Installer ([Main Page](https://litipk.github.io/Jupyter-PHP-Installer/)) 2 | 3 | [![Author](http://img.shields.io/badge/author-@castarco-blue.svg?style=flat-square)](https://twitter.com/castarco) 4 | [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) 5 | [![Packagist Version](https://img.shields.io/packagist/v/Litipk/jupyter-php.svg?style=flat-square)](https://packagist.org/packages/Litipk/jupyter-php) 6 | 7 | ## Introduction 8 | 9 | [**Jupyter-PHP**](https://github.com/Litipk/Jupyter-PHP) is a PHP kernel for [*Jupyter*](http://jupyter.org). This 10 | repository holds an installer to make easier installing the Jupyter-PHP kernel. 11 | 12 | ## Getting started 13 | 14 | Go to the [main page](https://litipk.github.io/Jupyter-PHP-Installer/) and follow the instructions. 15 | 16 | ## Learn more 17 | 18 | * [Chat Room](https://gitter.im/Litipk/Jupyter-PHP) : If you want to have a real-time chat with other Jupyter-PHP users or developers, you can do it here. 19 | * [Group / Mail List](https://groups.io/g/jupyter-php) : If a chat room isn't enough to post your doubts or ideas, you can join to our mail list. 20 | 21 | ## How to contribute 22 | 23 | * First of all, you can take a look on the [bugtracker](https://github.com/Litipk/Jupyter-PHP-Installer/issues) and decide if there is something that you want to do :wink: . If you think there are missing improvements in this file, then you are invited to modify the TODO list. 24 | * You can also send us bug reports using the same bugtracker. 25 | * If you are really interested on helping to improve Litipk\BigNumbers, we recommend to read the [contributing guidelines](https://github.com/Litipk/Jupyter-PHP-Installer/blob/master/CONTRIBUTING.md). 26 | 27 | 28 | ## License 29 | 30 | Jupyter-PHP and its installer are licensed under the [MIT License](https://github.com/Litipk/Jupyter-PHP-Installer/blob/master/LICENSE). 31 | -------------------------------------------------------------------------------- /bin/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "Starting build"; 4 | 5 | # We only generate packages for tagged versions 6 | git describe --tags --exact-match HEAD || { 7 | echo "Skipped build"; 8 | exit 0; 9 | } 10 | 11 | openssl aes-256-cbc -K $encrypted_f21abcc37842_key -iv $encrypted_f21abcc37842_iv -in .travis/secrets.tar.enc -out .travis/secrets.tar -d 12 | 13 | # Unpack secrets 14 | tar xvf .travis/secrets.tar -C .travis 15 | 16 | # Setup SSH agent: 17 | eval "$(ssh-agent -s)" #start the ssh agent 18 | chmod 600 .travis/deploy_private.key 19 | ssh-add .travis/deploy_private.key 20 | 21 | # Setup git defaults: 22 | git config --global user.email "castarco@gmail.com" 23 | git config --global user.name "Andreu Correa Casablanca" 24 | 25 | # Add SSH-based remote to GitHub repo: 26 | git remote add deploy git@github.com:Litipk/Jupyter-PHP-Installer.git 27 | git fetch deploy 28 | 29 | # Get Box and build 30 | wget https://github.com/box-project/box2/releases/download/2.7.0/box-2.7.0.phar -O ./bin/box.phar 31 | chmod a+x ./bin/box.phar 32 | 33 | # Unsigned build 34 | ./bin/box.phar build -vv 35 | mv jupyter-php-installer.phar jupyter-php-installer.phar.tmp 36 | 37 | # Signed build 38 | ./bin/box.phar build -c box.signed.json -vv 39 | mv jupyter-php-installer.phar jupyter-php-installer.signed.phar.tmp 40 | mv jupyter-php-installer.phar.pubkey jupyter-php-installer.signed.phar.pubkey.tmp 41 | 42 | # Checkout gh-pages and add PHAR file and version: 43 | git checkout -b gh-pages deploy/gh-pages 44 | 45 | # Moving unsigned build 46 | mv jupyter-php-installer.phar.tmp dist/jupyter-php-installer.phar 47 | cd dist && md5sum jupyter-php-installer.phar > jupyter-php-installer.phar.md5 && cd .. 48 | cd dist && sha1sum jupyter-php-installer.phar > jupyter-php-installer.phar.sha1 && cd .. 49 | cd dist && sha256sum jupyter-php-installer.phar > jupyter-php-installer.phar.sha256 && cd .. 50 | cd dist && sha512sum jupyter-php-installer.phar > jupyter-php-installer.phar.sha512 && cd .. 51 | 52 | # Moving signed build 53 | mv jupyter-php-installer.signed.phar.tmp dist/jupyter-php-installer.signed.phar 54 | mv jupyter-php-installer.signed.phar.pubkey.tmp dist/jupyter-php-installer.signed.phar.pubkey 55 | cd dist && md5sum jupyter-php-installer.signed.phar > jupyter-php-installer.signed.phar.md5 && cd .. 56 | cd dist && sha1sum jupyter-php-installer.signed.phar > jupyter-php-installer.signed.phar.sha1 && cd .. 57 | cd dist && sha256sum jupyter-php-installer.signed.phar > jupyter-php-installer.signed.phar.sha256 && cd .. 58 | cd dist && sha512sum jupyter-php-installer.signed.phar > jupyter-php-installer.signed.phar.sha512 && cd .. 59 | cd dist && md5sum jupyter-php-installer.signed.phar.pubkey > jupyter-php-installer.signed.phar.pubkey.md5 && cd .. 60 | cd dist && sha1sum jupyter-php-installer.signed.phar.pubkey > jupyter-php-installer.signed.phar.pubkey.sha1 && cd .. 61 | cd dist && sha256sum jupyter-php-installer.signed.phar.pubkey > jupyter-php-installer.signed.phar.pubkey.sha256 && cd .. 62 | cd dist && sha512sum jupyter-php-installer.signed.phar.pubkey > jupyter-php-installer.signed.phar.pubkey.sha512 && cd .. 63 | 64 | # Adding phar files 65 | git add dist/jupyter-php-installer.phar dist/jupyter-php-installer.signed.phar 66 | 67 | # Adding public keys 68 | git add dist/jupyter-php-installer.signed.phar.pubkey 69 | 70 | # Adding "unsigned" checksums 71 | git add dist/jupyter-php-installer.phar.md5 dist/jupyter-php-installer.phar.sha1 dist/jupyter-php-installer.phar.sha256 dist/jupyter-php-installer.phar.sha512 72 | 73 | # Adding "signed" checksums 74 | git add dist/jupyter-php-installer.signed.phar.md5 dist/jupyter-php-installer.signed.phar.sha1 dist/jupyter-php-installer.signed.phar.sha256 dist/jupyter-php-installer.signed.phar.sha512 75 | 76 | # Adding public key checksums 77 | git add dist/jupyter-php-installer.signed.phar.pubkey.md5 dist/jupyter-php-installer.signed.phar.pubkey.sha1 dist/jupyter-php-installer.signed.phar.pubkey.sha256 dist/jupyter-php-installer.signed.phar.pubkey.sha512 78 | 79 | 80 | # Commit and push: 81 | git commit -m 'Rebuilt phar' 82 | git push deploy gh-pages:gh-pages -------------------------------------------------------------------------------- /box.json: -------------------------------------------------------------------------------- 1 | { 2 | "alias": "jupyter-php-installer.phar", 3 | "chmod": "0755", 4 | "compression": "GZ", 5 | "directories": ["src"], 6 | "files": [ 7 | "LICENSE", 8 | "README.md" 9 | ], 10 | "finder": [ 11 | { 12 | "name": "*.php", 13 | "exclude": ["Tests", "Tester"], 14 | "in": "vendor" 15 | } 16 | ], 17 | "git-version": "package_version", 18 | "main": "src/EntryPoint/main.php", 19 | "output": "jupyter-php-installer.phar", 20 | "stub": true 21 | } -------------------------------------------------------------------------------- /box.signed.json: -------------------------------------------------------------------------------- 1 | { 2 | "alias": "jupyter-php-installer.phar", 3 | "chmod": "0755", 4 | "algorithm": "OPENSSL", 5 | "compression": "GZ", 6 | "directories": ["src"], 7 | "files": [ 8 | "LICENSE", 9 | "README.md" 10 | ], 11 | "finder": [ 12 | { 13 | "name": "*.php", 14 | "exclude": ["Tests", "Tester"], 15 | "in": "vendor" 16 | } 17 | ], 18 | "git-version": "package_version", 19 | "main": "src/EntryPoint/main.php", 20 | "output": "jupyter-php-installer.phar", 21 | "stub": true, 22 | "key": ".travis/phar_private.key" 23 | } -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "litipk/jupyter-php-installer", 3 | "description": "An installer for Jupyter-PHP", 4 | "type": "project", 5 | "license": "MIT", 6 | "require": { 7 | "php": ">=7.0", 8 | "symfony/console": "^3.0", 9 | "seld/cli-prompt": "^1.0" 10 | }, 11 | "require-dev": { 12 | "phpunit/phpunit": "^6.1" 13 | }, 14 | "autoload": { 15 | "psr-4": { "Litipk\\JupyterPhpInstaller\\": "src/"} 16 | }, 17 | "scripts": { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /phpunit.hhvm.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | ./tests/ 17 | 18 | 19 | 20 | 21 | ./src/ 22 | 23 | 24 | -------------------------------------------------------------------------------- /phpunit.php: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | ./tests/ 17 | 18 | 19 | 20 | 21 | ./src/ 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/Command/Command.php: -------------------------------------------------------------------------------- 1 | io) { 22 | $application = $this->getApplication(); 23 | 24 | if ($application instanceof Application) { 25 | $this->io = $application->getIO(); 26 | } else { 27 | $this->io = new NullIO(); 28 | } 29 | } 30 | 31 | return $this->io; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Command/InfoCommand.php: -------------------------------------------------------------------------------- 1 | setName('info') 19 | ->setDescription('Shows info about installed Jupyter-PHP kernels.') 20 | ->setDefinition([ 21 | new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details.'), 22 | new InputArgument( 23 | 'path', 24 | InputArgument::OPTIONAL, 25 | 'If is provided, then instead of looking for install paths, uses the given one.' 26 | ) 27 | ]) 28 | ->setHelp( 29 | "The info command looks for installed Jupyter-PHP kernels\n". 30 | "and shows info about them.\n\n". 31 | "php jupyter-php-installer.phar info\n\n" 32 | ); 33 | } 34 | 35 | protected function execute(InputInterface $input, OutputInterface $output) 36 | { 37 | 38 | } 39 | } -------------------------------------------------------------------------------- /src/Command/InstallCommand.php: -------------------------------------------------------------------------------- 1 | setName('install') 24 | ->setDescription('Installs a Jupyter-PHP kernel.') 25 | ->setDefinition([ 26 | new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details.'), 27 | new InputArgument( 28 | 'path', 29 | InputArgument::OPTIONAL, 30 | 'This is the path where the Jupyter-PHP kernel will be installed.' 31 | ), 32 | new InputArgument( 33 | 'composer_cmd', 34 | InputArgument::OPTIONAL, 35 | 'The installer will use this command to execute Composer.' 36 | ) 37 | ]) 38 | ->setHelp( 39 | "The install command installs a Jupyter-PHP kernel in your\n". 40 | "system and makes possible that Jupyter uses it to create \"PHP\n". 41 | "Notebooks\"\n\n". 42 | "php jupyter-php-installer.phar install\n\n" 43 | ); 44 | } 45 | 46 | protected function execute(InputInterface $input, OutputInterface $output) 47 | { 48 | $installPath = ($input->hasArgument('path')) ? $input->getArgument('path') : null; 49 | $composerCmd = ($input->hasArgument('composer_cmd')) ? $input->getArgument('composer_cmd') : null; 50 | 51 | $io = $this->getIO(); 52 | 53 | try { 54 | $installer = Installer::getInstaller($composerCmd); 55 | } catch (\RuntimeException $e) { 56 | $io->writeError('ERROR: '.$e->getMessage()); 57 | // TODO : Use verbosity levels to enable showing the stacktrace 58 | return self::RETURN_CODE_INSTALLER_INSTANTIATION_ERROR; 59 | } 60 | 61 | try { 62 | $installer->install($installPath, (bool)$input->getOption('verbose')); 63 | } catch (\RuntimeException $e) { 64 | $io->writeError('ERROR: '.$e->getMessage()); 65 | // TODO : Use verbosity levels to enable showing the stacktrace 66 | return self::RETURN_CODE_INSTALLER_INSTALL_ERROR; 67 | } 68 | 69 | $io->write('The Jupyter-PHP kernel has been successfully installed.'); 70 | 71 | return self::RETURN_CODE_OK; 72 | } 73 | } -------------------------------------------------------------------------------- /src/Command/UninstallCommand.php: -------------------------------------------------------------------------------- 1 | setName('uninstall') 19 | ->setDescription('Uninstalls a Jupyter-PHP kernel.') 20 | ->setDefinition([ 21 | new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details.'), 22 | new InputArgument( 23 | 'path', 24 | InputArgument::OPTIONAL, 25 | 'This is the path where the Jupyter-PHP kernel is installed.' 26 | ), 27 | new InputArgument( 28 | 'composer_cmd', 29 | InputArgument::OPTIONAL, 30 | 'The installer will use this command to execute Composer.' 31 | ) 32 | ]) 33 | ->setHelp( 34 | "The uninstall command uninstalls a Jupyter-PHP kernel from your\n". 35 | "system.\n\n". 36 | "php jupyter-php-installer.phar uninstall\n\n" 37 | ); 38 | } 39 | 40 | protected function execute(InputInterface $input, OutputInterface $output) 41 | { 42 | 43 | } 44 | } -------------------------------------------------------------------------------- /src/Command/UpdateCommand.php: -------------------------------------------------------------------------------- 1 | setName('update') 19 | ->setDescription('Updates the installed Jupyter-PHP kernel.') 20 | ->setDefinition([ 21 | new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details.'), 22 | new InputArgument( 23 | 'path', 24 | InputArgument::OPTIONAL, 25 | 'This is the path where the Jupyter-PHP kernel is installed.' 26 | ), 27 | new InputArgument( 28 | 'composer_cmd', 29 | InputArgument::OPTIONAL, 30 | 'The installer will use this command to execute Composer.' 31 | ) 32 | ]) 33 | ->setHelp("The update command updates a previously installed Jupyter-PHP\n". 34 | "kernel.\n\n". 35 | "php jupyter-php-installer.phar pdate\n\n" 36 | ); 37 | } 38 | 39 | protected function execute(InputInterface $input, OutputInterface $output) 40 | { 41 | 42 | } 43 | } -------------------------------------------------------------------------------- /src/Console/Application.php: -------------------------------------------------------------------------------- 1 | handleShutdown(); }); 46 | $shutdownRegistered = true; 47 | } 48 | 49 | parent::__construct('Jupyter-PHP Installer', '0.2'); 50 | } 51 | 52 | /** 53 | * Starts the application, boilerplate code. 54 | * @param InputInterface|null $input 55 | * @param OutputInterface|null $output 56 | * @return int 57 | */ 58 | public function run(InputInterface $input = null, OutputInterface $output = null) 59 | { 60 | if (null === $output) { 61 | $output = new ConsoleOutput( 62 | ConsoleOutput::VERBOSITY_NORMAL, 63 | null, 64 | new OutputFormatter(false, [ 65 | 'highlight' => new OutputFormatterStyle('red'), 66 | 'warning' => new OutputFormatterStyle('black', 'yellow'), 67 | ]) 68 | ); 69 | } 70 | 71 | return parent::run($input, $output); 72 | } 73 | 74 | /** 75 | * Runs the application's business logic. 76 | * @param InputInterface $input 77 | * @param OutputInterface $output 78 | * @return int 79 | */ 80 | public function doRun(InputInterface $input, OutputInterface $output) 81 | { 82 | $this->io = new ConsoleIO($input, $output, $this->getHelperSet()); 83 | ErrorHandler::register($this->io); 84 | $io = $this->io; 85 | 86 | if (PHP_VERSION_ID < 70000) { 87 | $io->writeError( 88 | ''. 89 | 'This installer only officially supports PHP 7.0 and above, you will most likely encounter problems with your PHP '. 90 | PHP_VERSION.', upgrading is strongly recommended'. 91 | '' 92 | ); 93 | } 94 | 95 | if (extension_loaded('xdebug')) { 96 | $io->writeError( 97 | ''. 98 | 'You are running PHP-CLI with xdebug enabled. This will have a major impact on the kernel\'s performance.'. 99 | '' 100 | ); 101 | } 102 | 103 | return parent::doRun($input, $output); 104 | } 105 | 106 | /** 107 | * @return IOInterface 108 | */ 109 | public function getIO() 110 | { 111 | return $this->io; 112 | } 113 | 114 | /** 115 | * Initializes all the composer commands. 116 | */ 117 | protected function getDefaultCommands() 118 | { 119 | $commands = parent::getDefaultCommands(); 120 | $commands[] = new InstallCommand(); 121 | $commands[] = new UninstallCommand(); 122 | $commands[] = new UpdateCommand(); 123 | $commands[] = new InfoCommand(); 124 | 125 | 126 | if ('phar:' === substr(__FILE__, 0, 5)) { 127 | // Nothing to do, yet 128 | } 129 | 130 | return $commands; 131 | } 132 | 133 | /** 134 | * Handles the program shutdown. 135 | */ 136 | private function handleShutdown() 137 | { 138 | $lastError = error_get_last(); 139 | 140 | if (!empty($lastError) && isset($lastError['message'])) { 141 | $this->handleShutdownLastError($lastError); 142 | } 143 | } 144 | 145 | /** 146 | * Handles the errors that caused the shutdown. 147 | * @param array $lastError 148 | */ 149 | private function handleShutdownLastError(array $lastError) 150 | { 151 | if ( 152 | strpos($lastError['message'], 'Allowed memory') !== false /* Zend PHP out of memory error */ || 153 | strpos($lastError['message'], 'exceeded memory') !== false /* HHVM out of memory errors */ 154 | ) { 155 | $this->explainMemoryErrorSolution(); 156 | } 157 | } 158 | 159 | /** 160 | * Explains to the user what can be done to solve the memory error that caused the shutdown. 161 | */ 162 | private function explainMemoryErrorSolution() 163 | { 164 | if (extension_loaded('xdebug')) { 165 | echo "\n" . 'You should disable the Xdebug extension in your php.ini settings file.' . "\n"; 166 | } else { 167 | echo "\n" . 'You should increase the `memory_limit` parameter in your php.ini settings file.' . "\n"; 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/EntryPoint/bootstrap.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 | function includeIfExists($file) 13 | { 14 | return file_exists($file) ? include $file : false; 15 | } 16 | 17 | if ((!$loader = includeIfExists(__DIR__ . '/../../vendor/autoload.php'))) { 18 | echo 'The dependencies are missing, you should use `composer install`.'.PHP_EOL; 19 | exit(1); 20 | } 21 | 22 | return $loader; 23 | -------------------------------------------------------------------------------- /src/EntryPoint/main.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | run(); 17 | -------------------------------------------------------------------------------- /src/IO/ConsoleIO.php: -------------------------------------------------------------------------------- 1 | 7 | * Jordi Boggiano 8 | * Andres Correa 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Litipk\JupyterPhpInstaller\IO; 15 | 16 | use Symfony\Component\Console\Input\InputInterface; 17 | use Symfony\Component\Console\Output\ConsoleOutputInterface; 18 | use Symfony\Component\Console\Output\OutputInterface; 19 | use Symfony\Component\Console\Helper\HelperSet; 20 | use Symfony\Component\Console\Question\ConfirmationQuestion; 21 | use Symfony\Component\Console\Question\Question; 22 | 23 | /** 24 | * The Input/Output helper. 25 | * 26 | * @author François Pluchino 27 | * @author Jordi Boggiano 28 | * @author Andres Correa 29 | */ 30 | final class ConsoleIO implements IOInterface 31 | { 32 | protected $input; 33 | protected $output; 34 | protected $helperSet; 35 | protected $lastMessage; 36 | protected $lastMessageErr; 37 | private $startTime; 38 | 39 | /** 40 | * Constructor. 41 | * 42 | * @param InputInterface $input The input instance 43 | * @param OutputInterface $output The output instance 44 | * @param HelperSet $helperSet The helperSet instance 45 | */ 46 | public function __construct(InputInterface $input, OutputInterface $output, HelperSet $helperSet) 47 | { 48 | $this->input = $input; 49 | $this->output = $output; 50 | $this->helperSet = $helperSet; 51 | } 52 | 53 | public function enableDebugging($startTime) 54 | { 55 | $this->startTime = $startTime; 56 | } 57 | 58 | /** 59 | * {@inheritDoc} 60 | */ 61 | public function isInteractive() 62 | { 63 | return $this->input->isInteractive(); 64 | } 65 | 66 | /** 67 | * {@inheritDoc} 68 | */ 69 | public function isDecorated() 70 | { 71 | return $this->output->isDecorated(); 72 | } 73 | 74 | /** 75 | * {@inheritDoc} 76 | */ 77 | public function isVerbose() 78 | { 79 | return $this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE; 80 | } 81 | 82 | /** 83 | * {@inheritDoc} 84 | */ 85 | public function isVeryVerbose() 86 | { 87 | return $this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERY_VERBOSE; 88 | } 89 | 90 | /** 91 | * {@inheritDoc} 92 | */ 93 | public function isDebug() 94 | { 95 | return $this->output->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG; 96 | } 97 | 98 | /** 99 | * {@inheritDoc} 100 | */ 101 | public function write($messages, $newline = true) 102 | { 103 | $this->doWrite($messages, $newline, false); 104 | } 105 | 106 | /** 107 | * {@inheritDoc} 108 | */ 109 | public function writeError($messages, $newline = true) 110 | { 111 | $this->doWrite($messages, $newline, true); 112 | } 113 | 114 | /** 115 | * @param array $messages 116 | * @param bool $newline 117 | * @param bool $stderr 118 | */ 119 | private function doWrite($messages, $newline, $stderr) 120 | { 121 | if (null !== $this->startTime) { 122 | $memoryUsage = memory_get_usage() / 1024 / 1024; 123 | $timeSpent = microtime(true) - $this->startTime; 124 | $messages = array_map(function ($message) use ($memoryUsage, $timeSpent) { 125 | return sprintf('[%.1fMB/%.2fs] %s', $memoryUsage, $timeSpent, $message); 126 | }, (array) $messages); 127 | } 128 | 129 | if (true === $stderr && $this->output instanceof ConsoleOutputInterface) { 130 | $this->output->getErrorOutput()->write($messages, $newline); 131 | $this->lastMessageErr = join($newline ? "\n" : '', (array) $messages); 132 | 133 | return; 134 | } 135 | 136 | $this->output->write($messages, $newline); 137 | $this->lastMessage = join($newline ? "\n" : '', (array) $messages); 138 | } 139 | 140 | /** 141 | * {@inheritDoc} 142 | */ 143 | public function overwrite($messages, $newline = true, $size = null) 144 | { 145 | $this->doOverwrite($messages, $newline, $size, false); 146 | } 147 | 148 | /** 149 | * {@inheritDoc} 150 | */ 151 | public function overwriteError($messages, $newline = true, $size = null) 152 | { 153 | $this->doOverwrite($messages, $newline, $size, true); 154 | } 155 | 156 | /** 157 | * @param array $messages 158 | * @param bool $newline 159 | * @param int $size 160 | * @param bool $stderr 161 | */ 162 | private function doOverwrite($messages, $newline, $size, $stderr) 163 | { 164 | // messages can be an array, let's convert it to string anyway 165 | $messages = join($newline ? "\n" : '', (array) $messages); 166 | 167 | // since overwrite is supposed to overwrite last message... 168 | if (!isset($size)) { 169 | // removing possible formatting of lastMessage with strip_tags 170 | $size = strlen(strip_tags($stderr ? $this->lastMessageErr : $this->lastMessage)); 171 | } 172 | // ...let's fill its length with backspaces 173 | $this->doWrite(str_repeat("\x08", $size), false, $stderr); 174 | 175 | // write the new message 176 | $this->doWrite($messages, false, $stderr); 177 | 178 | $fill = $size - strlen(strip_tags($messages)); 179 | if ($fill > 0) { 180 | // whitespace whatever has left 181 | $this->doWrite(str_repeat(' ', $fill), false, $stderr); 182 | // move the cursor back 183 | $this->doWrite(str_repeat("\x08", $fill), false, $stderr); 184 | } 185 | 186 | if ($newline) { 187 | $this->doWrite('', true, $stderr); 188 | } 189 | 190 | if ($stderr) { 191 | $this->lastMessageErr = $messages; 192 | } else { 193 | $this->lastMessage = $messages; 194 | } 195 | } 196 | 197 | /** 198 | * {@inheritDoc} 199 | */ 200 | public function ask($question, $default = null) 201 | { 202 | $output = $this->output; 203 | 204 | if ($output instanceof ConsoleOutputInterface) { 205 | $output = $output->getErrorOutput(); 206 | } 207 | 208 | /** @var \Symfony\Component\Console\Helper\QuestionHelper $helper */ 209 | $helper = $this->helperSet->get('question'); 210 | $question = new Question($question, $default); 211 | 212 | return $helper->ask($this->input, $output, $question); 213 | } 214 | 215 | /** 216 | * {@inheritDoc} 217 | */ 218 | public function askConfirmation($question, $default = true) 219 | { 220 | $output = $this->output; 221 | 222 | if ($output instanceof ConsoleOutputInterface) { 223 | $output = $output->getErrorOutput(); 224 | } 225 | 226 | /** @var \Symfony\Component\Console\Helper\QuestionHelper $helper */ 227 | $helper = $this->helperSet->get('question'); 228 | $question = new ConfirmationQuestion($question, $default); 229 | 230 | return $helper->ask($this->input, $output, $question); 231 | } 232 | 233 | /** 234 | * {@inheritDoc} 235 | */ 236 | public function askAndValidate($question, $validator, $attempts = null, $default = null) 237 | { 238 | $output = $this->output; 239 | 240 | if ($output instanceof ConsoleOutputInterface) { 241 | $output = $output->getErrorOutput(); 242 | } 243 | 244 | /** @var \Symfony\Component\Console\Helper\QuestionHelper $helper */ 245 | $helper = $this->helperSet->get('question'); 246 | $question = new Question($question, $default); 247 | $question->setValidator($validator); 248 | $question->setMaxAttempts($attempts); 249 | 250 | return $helper->ask($this->input, $output, $question); 251 | } 252 | 253 | /** 254 | * {@inheritDoc} 255 | */ 256 | public function askAndHideAnswer($question) 257 | { 258 | $this->writeError($question, false); 259 | 260 | return \Seld\CliPrompt\CliPrompt::hiddenPrompt(true); 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /src/IO/IOInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * Jordi Boggiano 8 | * Andres Correa 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | 15 | namespace Litipk\JupyterPhpInstaller\IO; 16 | 17 | 18 | /** 19 | * The Input/Output helper interface. 20 | * 21 | * @author François Pluchino 22 | * @author Andres Correa 23 | */ 24 | interface IOInterface 25 | { 26 | /** 27 | * Is this input means interactive? 28 | * 29 | * @return bool 30 | */ 31 | public function isInteractive(); 32 | 33 | /** 34 | * Is this output verbose? 35 | * 36 | * @return bool 37 | */ 38 | public function isVerbose(); 39 | 40 | /** 41 | * Is the output very verbose? 42 | * 43 | * @return bool 44 | */ 45 | public function isVeryVerbose(); 46 | 47 | /** 48 | * Is the output in debug verbosity? 49 | * 50 | * @return bool 51 | */ 52 | public function isDebug(); 53 | 54 | /** 55 | * Is this output decorated? 56 | * 57 | * @return bool 58 | */ 59 | public function isDecorated(); 60 | 61 | /** 62 | * Writes a message to the output. 63 | * 64 | * @param string|array $messages The message as an array of lines or a single string 65 | * @param bool $newline Whether to add a newline or not 66 | */ 67 | public function write($messages, $newline = true); 68 | 69 | /** 70 | * Writes a message to the error output. 71 | * 72 | * @param string|array $messages The message as an array of lines or a single string 73 | * @param bool $newline Whether to add a newline or not 74 | */ 75 | public function writeError($messages, $newline = true); 76 | 77 | /** 78 | * Overwrites a previous message to the output. 79 | * 80 | * @param string|array $messages The message as an array of lines or a single string 81 | * @param bool $newline Whether to add a newline or not 82 | * @param int $size The size of line 83 | */ 84 | public function overwrite($messages, $newline = true, $size = null); 85 | 86 | /** 87 | * Overwrites a previous message to the error output. 88 | * 89 | * @param string|array $messages The message as an array of lines or a single string 90 | * @param bool $newline Whether to add a newline or not 91 | * @param int $size The size of line 92 | */ 93 | public function overwriteError($messages, $newline = true, $size = null); 94 | 95 | /** 96 | * Asks a question to the user. 97 | * 98 | * @param string|array $question The question to ask 99 | * @param string $default The default answer if none is given by the user 100 | * 101 | * @throws \RuntimeException If there is no data to read in the input stream 102 | * @return string The user answer 103 | */ 104 | public function ask($question, $default = null); 105 | 106 | /** 107 | * Asks a confirmation to the user. 108 | * 109 | * The question will be asked until the user answers by nothing, yes, or no. 110 | * 111 | * @param string|array $question The question to ask 112 | * @param bool $default The default answer if the user enters nothing 113 | * 114 | * @return bool true if the user has confirmed, false otherwise 115 | */ 116 | public function askConfirmation($question, $default = true); 117 | 118 | /** 119 | * Asks for a value and validates the response. 120 | * 121 | * The validator receives the data to validate. It must return the 122 | * validated data when the data is valid and throw an exception 123 | * otherwise. 124 | * 125 | * @param string|array $question The question to ask 126 | * @param callback $validator A PHP callback 127 | * @param null|int $attempts Max number of times to ask before giving up (default of null means infinite) 128 | * @param mixed $default The default answer if none is given by the user 129 | * 130 | * @throws \Exception When any of the validators return an error 131 | * @return mixed 132 | */ 133 | public function askAndValidate($question, $validator, $attempts = null, $default = null); 134 | 135 | /** 136 | * Asks a question to the user and hide the answer. 137 | * 138 | * @param string $question The question to ask 139 | * 140 | * @return string The answer 141 | */ 142 | public function askAndHideAnswer($question); 143 | } 144 | -------------------------------------------------------------------------------- /src/IO/NullIO.php: -------------------------------------------------------------------------------- 1 | 7 | * Jordi Boggiano 8 | * Andres Correa 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace Litipk\JupyterPhpInstaller\IO; 15 | 16 | /** 17 | * IOInterface that is not interactive and never writes the output 18 | * 19 | * @author Christophe Coevoet 20 | */ 21 | class NullIO implements IOInterface 22 | { 23 | /** 24 | * {@inheritDoc} 25 | */ 26 | public function isInteractive() 27 | { 28 | return false; 29 | } 30 | 31 | /** 32 | * {@inheritDoc} 33 | */ 34 | public function isVerbose() 35 | { 36 | return false; 37 | } 38 | 39 | /** 40 | * {@inheritDoc} 41 | */ 42 | public function isVeryVerbose() 43 | { 44 | return false; 45 | } 46 | 47 | /** 48 | * {@inheritDoc} 49 | */ 50 | public function isDebug() 51 | { 52 | return false; 53 | } 54 | 55 | /** 56 | * {@inheritDoc} 57 | */ 58 | public function isDecorated() 59 | { 60 | return false; 61 | } 62 | 63 | /** 64 | * {@inheritDoc} 65 | */ 66 | public function write($messages, $newline = true) 67 | { 68 | } 69 | 70 | /** 71 | * {@inheritDoc} 72 | */ 73 | public function writeError($messages, $newline = true) 74 | { 75 | } 76 | 77 | /** 78 | * {@inheritDoc} 79 | */ 80 | public function overwrite($messages, $newline = true, $size = 80) 81 | { 82 | } 83 | 84 | /** 85 | * {@inheritDoc} 86 | */ 87 | public function overwriteError($messages, $newline = true, $size = 80) 88 | { 89 | } 90 | 91 | /** 92 | * {@inheritDoc} 93 | */ 94 | public function ask($question, $default = null) 95 | { 96 | return $default; 97 | } 98 | 99 | /** 100 | * {@inheritDoc} 101 | */ 102 | public function askConfirmation($question, $default = true) 103 | { 104 | return $default; 105 | } 106 | 107 | /** 108 | * {@inheritDoc} 109 | */ 110 | public function askAndValidate($question, $validator, $attempts = false, $default = null) 111 | { 112 | return $default; 113 | } 114 | 115 | /** 116 | * {@inheritDoc} 117 | */ 118 | public function askAndHideAnswer($question) 119 | { 120 | return null; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/Installer/Installer.php: -------------------------------------------------------------------------------- 1 | getComposerCommand(); 25 | 26 | if (null === $composerCmd || !$system->checkIfCommandExists($composerCmd)) { 27 | throw new \RuntimeException('Unable to find the composer executable.'); 28 | } 29 | 30 | if ($system instanceof LinuxSystem) { 31 | return new LinuxInstaller($system, $composerCmd); 32 | } elseif ($system instanceof MacSystem) { 33 | return new MacInstaller($system, $composerCmd); 34 | } elseif ($system instanceof UnixSystem) { 35 | // Fallback for BSD systems 36 | return new LinuxInstaller($system, $composerCmd); 37 | } elseif ($system instanceof WindowsSystem) { 38 | return new WindowsInstaller($system, $composerCmd); 39 | } else { 40 | throw new \RuntimeException('This platform is unknown for the installer'); 41 | } 42 | } 43 | 44 | public function install(string $installPath = null, bool $beVerbose = false) 45 | { 46 | $installPath = (null !== $installPath) ? $installPath : $this->getInstallPath(); 47 | if (null !== $installPath && !$this->system->validatePath($installPath)) { 48 | throw new \RuntimeException('Invalid install path.'); 49 | } 50 | 51 | $this->system->ensurePath($installPath); 52 | $this->executeComposerCommand($installPath, $beVerbose); 53 | 54 | $this->installKernel(); 55 | } 56 | 57 | protected function getInstallPath(): string 58 | { 59 | return ($this->system->isRunningAsAdmin()) 60 | ? $this->getAdminInstallPath() 61 | : $this->getUserInstallPath(); 62 | } 63 | 64 | protected abstract function getAdminInstallPath(): string; 65 | 66 | protected abstract function getUserInstallPath(): string; 67 | 68 | protected function installKernel() 69 | { 70 | $kernelDef = json_encode([ 71 | 'argv' => [ 72 | 'php', 73 | $this->getKernelEntrypointPath(), 74 | '{connection_file}' 75 | ], 76 | 'display_name' => 'PHP', 77 | 'language' => 'php', 78 | 'env' => new \stdClass 79 | ]); 80 | 81 | $kernelSpecPath = ($this->system->isRunningAsAdmin()) 82 | ? $this->getJupyterKernelsMetadataAdminPath() 83 | : $this->getJupyterKernelsMetadatUserPath(); 84 | 85 | $this->system->ensurePath($kernelSpecPath); 86 | file_put_contents($kernelSpecPath.'/kernel.json', $kernelDef); 87 | } 88 | 89 | protected abstract function getKernelEntryPointPath(): string; 90 | 91 | protected abstract function getJupyterKernelsMetadataAdminPath(): string; 92 | 93 | protected abstract function getJupyterKernelsMetadatUserPath(): string; 94 | 95 | protected function executeComposerCommand(string $installPath, bool $beVerbose = false) 96 | { 97 | $composerStatus = 0; 98 | 99 | $pkgsDir = $installPath.DIRECTORY_SEPARATOR.'pkgs'; 100 | $this->preparePackagesDir($pkgsDir); 101 | 102 | if ($beVerbose) { 103 | echo "\n"; 104 | passthru( 105 | $this->system->wrapCommandToAttachEnvironmentVariable( 106 | 'PATH', getenv('PATH'), 107 | $this->getComposerInitCommand($pkgsDir) . ' && ' . 108 | $this->getComposerInstallCommand($pkgsDir) 109 | ), 110 | 111 | $composerStatus 112 | ); 113 | echo "\n"; 114 | } else { 115 | $composerStatus = $this->executeSilentComposerCommand($pkgsDir); 116 | } 117 | 118 | if (0 !== $composerStatus) { 119 | throw new \RuntimeException('Error while trying to download Jupyter-PHP dependencies with Composer.'); 120 | } 121 | } 122 | 123 | protected function executeSilentComposerCommand(string $pkgsDir) 124 | { 125 | $composerOutputLines = []; 126 | 127 | exec( 128 | $this->system->wrapCommandToAttachEnvironmentVariable( 129 | 'PATH', getenv('PATH'), 130 | $this->getComposerInitCommand($pkgsDir, true) . ' && ' . 131 | $this->getComposerInstallCommand($pkgsDir, true) 132 | ), 133 | 134 | $composerOutputLines, 135 | $composerStatus 136 | ); 137 | 138 | return $composerStatus; 139 | } 140 | 141 | private function getComposerInitCommand(string $pkgsDir, bool $silent = false): string 142 | { 143 | $cmd = ( 144 | $this->composerCmd . ' init ' . 145 | ' --no-interaction ' . 146 | ' --name=jupyter-php-instance ' . 147 | ' --type=project ' . 148 | ' --working-dir="' . $pkgsDir . '" ' . 149 | ' --require=litipk/jupyter-php=0.* ' 150 | ); 151 | 152 | return ($silent) 153 | ? $this->system->wrapCommandToNullifyItsOutput($cmd) 154 | : $cmd; 155 | } 156 | 157 | private function getComposerInstallCommand(string $pkgsDir, bool $silent = false): string 158 | { 159 | $cmd = ( 160 | $this->composerCmd . ' install ' . 161 | ' --no-interaction ' . 162 | ' --no-progress ' . 163 | ' --prefer-dist ' . 164 | ' --optimize-autoloader ' . 165 | ' --working-dir="' . $pkgsDir . '" ' 166 | ); 167 | 168 | return ($silent) 169 | ? $this->system->wrapCommandToNullifyItsOutput($cmd . ' --no-progress ') 170 | : $cmd; 171 | } 172 | 173 | protected function __construct(System $system, string $composerCmd) 174 | { 175 | $this->system = $system; 176 | $this->composerCmd = $composerCmd; 177 | } 178 | 179 | protected function preparePackagesDir(string $pkgsDir) 180 | { 181 | if (file_exists($pkgsDir)) { 182 | foreach ( 183 | new \RecursiveIteratorIterator( 184 | new \RecursiveDirectoryIterator($pkgsDir, \FilesystemIterator::SKIP_DOTS), 185 | \RecursiveIteratorIterator::CHILD_FIRST 186 | ) as $path 187 | ) { 188 | $path->isDir() && !$path->isLink() ? rmdir($path->getPathname()) : unlink($path->getPathname()); 189 | } 190 | rmdir($pkgsDir); 191 | } 192 | mkdir($pkgsDir); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/Installer/LinuxInstaller.php: -------------------------------------------------------------------------------- 1 | system->getCurrentUserHome().'/.jupyter-php'; 30 | } 31 | 32 | protected function getJupyterKernelsMetadatUserPath(): string 33 | { 34 | return $this->system->getCurrentUserHome().'/.local/share/jupyter/kernels/jupyter-php'; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Installer/MacInstaller.php: -------------------------------------------------------------------------------- 1 | system->getCurrentUserHome().'/Library/jupyter-php'; 25 | } 26 | 27 | protected function getJupyterKernelsMetadatUserPath(): string 28 | { 29 | return $this->system->getCurrentUserHome().'/Library/Jupyter/kernels/jupyter-php'; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Installer/UnixInstaller.php: -------------------------------------------------------------------------------- 1 | getInstallPath() . '/pkgs/vendor/litipk/jupyter-php/src/kernel.php'; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Installer/WindowsInstaller.php: -------------------------------------------------------------------------------- 1 | system->getProgramDataPath() . '\jupyter-php'; 26 | } 27 | 28 | protected function getUserInstallPath(): string 29 | { 30 | return $this->system->getCurrentUserHome() . '\.jupyter-php'; 31 | } 32 | 33 | protected function getKernelEntryPointPath(): string 34 | { 35 | return $this->getInstallPath() . '\pkgs\vendor\litipk\jupyter-php\src\kernel.php'; 36 | } 37 | 38 | protected function getJupyterKernelsMetadataAdminPath(): string 39 | { 40 | return $this->system->getProgramDataPath() . '\jupyter\kernels\jupyter-php'; 41 | } 42 | 43 | protected function getJupyterKernelsMetadatUserPath(): string 44 | { 45 | return $this->system->getAppDataPath() . '\jupyter\kernels\jupyter-php'; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/System/BsdSystem.php: -------------------------------------------------------------------------------- 1 | getCurrentUser() === $this->getAdminUser()); 37 | } 38 | 39 | public abstract function getOperativeSystem(): int; 40 | 41 | public abstract function getCurrentUser(): string; 42 | 43 | public abstract function getAdminUser(): string; 44 | 45 | public abstract function getCurrentUserHome(): string; 46 | 47 | public abstract function checkIfCommandExists(string $cmdName): bool; 48 | 49 | /** @return string|null */ 50 | public abstract function getComposerCommand(); 51 | 52 | /** 53 | * Returns true if the path is a "valid" path and is writable (event if the complete path does not yet exist). 54 | */ 55 | public abstract function validatePath(string $path): bool; 56 | 57 | /** 58 | * @param string $path 59 | * @return string The "absolute path" version of $path. 60 | */ 61 | public abstract function ensurePath(string $path): string; 62 | 63 | public abstract function wrapCommandToNullifyItsOutput(string $command): string; 64 | 65 | public abstract function wrapCommandToAttachEnvironmentVariable(string $varName, string $varValue, string $command); 66 | 67 | protected abstract function isAbsolutePath(string $path): bool; 68 | 69 | protected abstract function getAbsolutePath(string $path): string; 70 | 71 | private static function guessOperativeSystem(): int 72 | { 73 | $phpOS = strtolower(PHP_OS); 74 | 75 | if ('linux' === $phpOS) { 76 | return self::OS_LINUX; 77 | } elseif ('darwin' === $phpOS) { 78 | return self::OS_OSX; 79 | } elseif (in_array($phpOS, ['windows', 'winnt', 'win32'])) { 80 | return self::OS_WIN; 81 | } elseif (in_array($phpOS, ['freebsd', 'netbsd', 'openbsd'])) { 82 | return self::OS_BSD; 83 | } else { 84 | return self::OS_UNKNOWN; 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/System/UnixSystem.php: -------------------------------------------------------------------------------- 1 | checkIfCommandExists('whoami')) { 15 | return exec('whoami'); 16 | } else { 17 | throw new \RuntimeException('Unable to obtain the current username.'); 18 | } 19 | } 20 | 21 | public function getAdminUser(): string 22 | { 23 | return 'root'; 24 | } 25 | 26 | public function getCurrentUserHome(): string 27 | { 28 | if (function_exists('posix_getpwuid') && function_exists('posix_geteuid')) { 29 | $pwuData = posix_getpwuid(posix_geteuid()); 30 | return $pwuData['dir']; 31 | } elseif (function_exists('getenv') && false !== getenv('HOME')) { 32 | return getenv('HOME'); 33 | } else { 34 | throw new \RuntimeException('Unable to obtain the current user home directory.'); 35 | } 36 | } 37 | 38 | public function checkIfCommandExists(string $cmdName): bool 39 | { 40 | if (!function_exists('exec')) { 41 | return false; 42 | } 43 | 44 | $sysResponse = exec( 45 | 'PATH='.getenv('PATH').'; '. 46 | "if command -v ".$cmdName." >/dev/null 2>&1; then echo \"true\"; else echo \"false\"; fi;" 47 | ); 48 | 49 | return filter_var($sysResponse, FILTER_VALIDATE_BOOLEAN); 50 | } 51 | 52 | /** @return string|null */ 53 | public function getComposerCommand() 54 | { 55 | $potentialCommands = [ 56 | 'composer', 'composer.phar', './composer.phar', './composer', './bin/composer', './bin/composer.phar', 57 | './vendor/bin/composer', './vendor/bin/composer.phar', '../vendor/bin/composer', 58 | '../vendor/bin/composer.phar', '../../vendor/bin/composer', '../../vendor/bin/composer.phar' 59 | ]; 60 | 61 | foreach ($potentialCommands as $potentialCommand) { 62 | if ($this->checkIfCommandExists($potentialCommand)) { 63 | return $potentialCommand; 64 | } 65 | } 66 | 67 | return null; 68 | } 69 | 70 | /** 71 | * Returns true if the path is a "valid" path and is writable (even if the complete path does not yet exist). 72 | */ 73 | public function validatePath(string $path): bool 74 | { 75 | $absPath = $this->getAbsolutePath($path); 76 | $absPathParts = preg_split('/\//', preg_replace('/(^\/|\/$)/', '', $absPath)); 77 | $nSteps = count($absPathParts); 78 | 79 | $tmpPath = ''; 80 | $prevReadable = false; 81 | $prevWritable = false; 82 | 83 | for ($i=0; $i<$nSteps; $i++) { 84 | $tmpPath .= '/' . $absPathParts[$i]; 85 | 86 | if (file_exists($tmpPath)) { 87 | if (!is_dir($tmpPath)) { 88 | if (is_link($tmpPath)) { 89 | $linkPath = readlink($tmpPath); 90 | if (false === $linkPath || !is_dir($linkPath)) { 91 | return false; 92 | } 93 | $tmpPath = $linkPath; 94 | } else { 95 | return false; 96 | } 97 | } 98 | 99 | $prevReadable = is_readable($tmpPath); 100 | $prevWritable = is_writable($tmpPath); 101 | } else { 102 | return ($prevReadable && $prevWritable); 103 | } 104 | } 105 | 106 | return true; 107 | } 108 | 109 | /** 110 | * @param string $path 111 | * @return string The "absolute path" version of $path. 112 | */ 113 | public function ensurePath(string $path): string 114 | { 115 | $absPath = $this->getAbsolutePath($path); 116 | 117 | if (!file_exists($absPath) && false === mkdir($absPath, 0755, true)) { 118 | throw new \RuntimeException('Unable to create the specified directory ('.$absPath.').'); 119 | } 120 | 121 | return $absPath; 122 | } 123 | 124 | public function wrapCommandToNullifyItsOutput(string $command): string 125 | { 126 | return $command . ' > /dev/null 2>&1 '; 127 | } 128 | 129 | public function wrapCommandToAttachEnvironmentVariable(string $varName, string $varValue, string $command) 130 | { 131 | return ' ' . $varName . '=' . $varValue . ' && ' . $command; 132 | } 133 | 134 | protected function isAbsolutePath(string $path): bool 135 | { 136 | return (1 === preg_match('#^/#', $path)); 137 | } 138 | 139 | protected function getAbsolutePath(string $path): string 140 | { 141 | return $this->isAbsolutePath($path) ? $path : (getcwd() . DIRECTORY_SEPARATOR . $path); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/System/WindowsSystem.php: -------------------------------------------------------------------------------- 1 | nul 2>&1 && echo true"); 44 | 45 | return filter_var($sysResponse, FILTER_VALIDATE_BOOLEAN); 46 | } 47 | 48 | public function getComposerCommand(): string 49 | { 50 | return 'composer'; 51 | } 52 | 53 | /** 54 | * Returns true if the path is a "valid" path and is writable (event if the complete path does not yet exist). 55 | */ 56 | public function validatePath(string $path): bool 57 | { 58 | $absPath = $this->getAbsolutePath($path); 59 | $absPathParts = explode(DIRECTORY_SEPARATOR, $absPath); 60 | $nSteps = count($absPathParts); 61 | 62 | $tmpPath = $absPathParts[0]; 63 | $prevReadable = false; 64 | $prevWritable = false; 65 | 66 | for ($i = 1; $i < $nSteps; $i++) { 67 | $tmpPath .= DIRECTORY_SEPARATOR . $absPathParts[$i]; 68 | 69 | if (file_exists($tmpPath)) { 70 | if (!is_dir($tmpPath)) { 71 | if (is_link($tmpPath)) { 72 | $linkPath = readlink($tmpPath); 73 | if (false === $linkPath || !is_dir($linkPath)) { 74 | return false; 75 | } 76 | $tmpPath = $linkPath; 77 | } else { 78 | return false; 79 | } 80 | } 81 | 82 | $prevReadable = is_readable($tmpPath); 83 | $prevWritable = is_writable($tmpPath); 84 | } else { 85 | return ($prevReadable && $prevWritable); 86 | } 87 | } 88 | 89 | return true; 90 | } 91 | 92 | /** 93 | * @param string $path 94 | * @return string The "absolute path" version of $path. 95 | */ 96 | public function ensurePath(string $path): string 97 | { 98 | $absPath = $this->getAbsolutePath($path); 99 | 100 | if (!file_exists($absPath) && false === mkdir($absPath, 0755, true)) { 101 | throw new \RuntimeException('Unable to create the specified directory (' . $absPath . ').'); 102 | } 103 | 104 | return $absPath; 105 | } 106 | 107 | public function wrapCommandToNullifyItsOutput(string $command): string 108 | { 109 | return $command . ' > nul 2>&1 '; 110 | } 111 | 112 | public function wrapCommandToAttachEnvironmentVariable(string $varName, string $varValue, string $command) 113 | { 114 | return ' set ' . $varName . '=' . $varValue . ' && ' . $command; 115 | } 116 | 117 | public function getProgramDataPath(): string 118 | { 119 | if (function_exists('getenv') && false !== getenv('PROGRAMDATA')) { 120 | return getenv('PROGRAMDATA'); 121 | } else { 122 | throw new \RuntimeException('Unable to obtain the program data directory.'); 123 | } 124 | } 125 | 126 | public function getAppDataPath(): string 127 | { 128 | if (function_exists('getenv') && false !== getenv('APPDATA')) { 129 | return getenv('APPDATA'); 130 | } else { 131 | throw new \RuntimeException('Unable to obtain the app data directory.'); 132 | } 133 | } 134 | 135 | protected function isAbsolutePath(string $path): bool 136 | { 137 | return preg_match('/^[a-z]\:/i', $path) === 1; 138 | } 139 | 140 | protected function getAbsolutePath(string $path): string 141 | { 142 | $path = $this->isAbsolutePath($path) ? $path : (getcwd() . DIRECTORY_SEPARATOR . $path); 143 | 144 | // Normalise directory separators 145 | $path = preg_replace('/[\/\\\\]/u', DIRECTORY_SEPARATOR, $path); 146 | 147 | return $path; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/Util/ErrorHandler.php: -------------------------------------------------------------------------------- 1 | 7 | * Jordi Boggiano 8 | * Andres Correa 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | 15 | namespace Litipk\JupyterPhpInstaller\Util; 16 | 17 | 18 | use Litipk\JupyterPhpInstaller\IO\IOInterface; 19 | 20 | 21 | /** 22 | * Convert PHP errors into exceptions 23 | * 24 | * @author Artem Lopata 25 | * @author Andres Correa 26 | */ 27 | class ErrorHandler 28 | { 29 | /** @var IOInterface */ 30 | private static $io; 31 | 32 | /** 33 | * Error handler 34 | * 35 | * @param int $level Level of the error raised 36 | * @param string $message Error message 37 | * @param string $file Filename that the error was raised in 38 | * @param int $line Line number the error was raised at 39 | * 40 | * @static 41 | * @throws \ErrorException 42 | */ 43 | public static function handle($level, $message, $file, $line) 44 | { 45 | // respect error_reporting being disabled 46 | if (!error_reporting()) { 47 | return; 48 | } 49 | 50 | if (ini_get('xdebug.scream')) { 51 | $message .= "\n\nWarning: You have xdebug.scream enabled, the warning above may be". 52 | "\na legitimately suppressed error that you were not supposed to see."; 53 | } 54 | 55 | if ($level !== E_DEPRECATED && $level !== E_USER_DEPRECATED) { 56 | throw new \ErrorException($message, 0, $level, $file, $line); 57 | } 58 | 59 | if (self::$io) { 60 | self::$io->writeError('Deprecation Notice: '.$message.' in '.$file.':'.$line.''); 61 | if (self::$io->isVerbose()) { 62 | self::$io->writeError('Stack trace:'); 63 | self::$io->writeError(array_filter(array_map(function ($a) { 64 | if (isset($a['line'], $a['file'])) { 65 | return ' '.$a['file'].':'.$a['line'].''; 66 | } 67 | 68 | return null; 69 | }, array_slice(debug_backtrace(), 2)))); 70 | } 71 | } 72 | } 73 | 74 | /** 75 | * Register error handler 76 | * 77 | * @static 78 | * @param IOInterface|null $io 79 | */ 80 | public static function register(IOInterface $io = null) 81 | { 82 | set_error_handler(array(__CLASS__, 'handle')); 83 | self::$io = $io; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /tests/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coder-Spirit/Jupyter-PHP-Installer/ac86a042b6db8fd196062775836831bfe675b0a7/tests/.gitkeep --------------------------------------------------------------------------------