diff --git a/README.md b/README.md index b519938e..daa56c25 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This package requires PHP 7.3 or higher in order to run the tool. You can use th **Warning:** This package is very experimental and breaking changes are very likely until version 1.0.0 is tagged. Use with caution, always wear a helmet when using this in production environments. ## Installation -Mozart brings its own dependencies to the table and that potentially introduces its own problems (yes, I realise how meta that is, for a package like this). That's why installing Mozart in isolation, either through the Docker container, the available PHAR file or installing Mozart as a global dependency with Composer is prefered. In all cases, the [configuration](#configuration) still needs to be placed in the `composer.json` file of the project iself. +Mozart brings its own dependencies to the table and that potentially introduces its own problems (yes, I realise how meta that is, for a package like this). That's why installing Mozart in isolation, either through the Docker container, the available PHAR file or installing Mozart as a global dependency with Composer is preferred. In all cases, the [configuration](#configuration) still needs to be placed in the `composer.json` file of the project iself. ### Docker Pull the Docker image from the registry: @@ -133,6 +133,8 @@ Mozart is designed to install and be forgotten about. Using Composer scripts, th When using Mozart through its Docker container, you can replace the `"\"vendor/bin/mozart\" compose",` lines with the actual commands you use to [run the Docker container](#docker) for your specific project. Running Mozart from inside the Docker container is really fast and shouldn't take more than a couple seconds. +If your plugin does not use Composer's autoloader, classmaps can be generated in the `dep_directory` and `classmap_directory` by running `vendor/bin/mozart dump-autoload` + ## Background and philosophy Mozart is designed to bridge the gap between the WordPress ecosytem and the vast packages ecosystem of PHP as a whole. Since WordPress is such an end-user friendly focussed CMS (for good reasons), there is no place within the ecosystem where an end-user would be burdened with using a developers tool like Composer. Also, since WordPress has to run on basically any hosting infrastructure, running Composer to install packages from the administration panel (trust me, I've tried - it can be done) is a mission impossible to make it happen and compatible with every server out there. diff --git a/composer.json b/composer.json index c97d2253..db7d3efd 100644 --- a/composer.json +++ b/composer.json @@ -12,6 +12,7 @@ "prefer-stable": true, "license": "MIT", "require": { + "composer/composer": "*", "php": "^7.3|^8.0", "symfony/console": "^4|^5", "symfony/finder": "^4|^5", diff --git a/src/Console/Application.php b/src/Console/Application.php index caa82dd3..75d9323c 100644 --- a/src/Console/Application.php +++ b/src/Console/Application.php @@ -3,6 +3,7 @@ namespace CoenJacobs\Mozart\Console; use CoenJacobs\Mozart\Console\Commands\Compose; +use CoenJacobs\Mozart\Console\Commands\DumpAutoload; use Symfony\Component\Console\Application as BaseApplication; class Application extends BaseApplication @@ -16,5 +17,8 @@ public function __construct($version) $composeCommand = new Compose(); $this->add($composeCommand); + + $dumpAutoloadCommand = new DumpAutoload(); + $this->add($dumpAutoloadCommand); } } diff --git a/src/Console/Commands/DumpAutoload.php b/src/Console/Commands/DumpAutoload.php new file mode 100644 index 00000000..96d89469 --- /dev/null +++ b/src/Console/Commands/DumpAutoload.php @@ -0,0 +1,125 @@ +setName('dump-autoload'); + $this->setDescription('Generates a classmap from files in the Mozart classmap and dep directories.'); + $this->setHelp(''); + } + + protected function readConfig(): object + { + $workingDir = getcwd(); + + $composerFile = $workingDir . DIRECTORY_SEPARATOR . 'composer.json'; + if (! file_exists($composerFile)) { + throw new Exception('No composer.json found at current directory: ' . $workingDir); + } + + $composer = json_decode(file_get_contents($composerFile)); + // If the json was malformed. + if (! is_object($composer)) { + throw new Exception('Unable to parse composer.json read at: ' . $workingDir); + } + + // if `extra` is missing or not an object or if it does not have a `mozart` key which is an object. + if (! isset($composer->extra) || ! is_object($composer->extra) + || ! isset($composer->extra->mozart) || ! is_object($composer->extra->mozart)) { + throw new Exception('Mozart config not readable in composer.json at extra->mozart'); + } + $config = $composer->extra->mozart; + + $config->dep_namespace = preg_replace("/\\\{2,}$/", "\\", "$config->dep_namespace\\"); + + return $config; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + try { + $this->config = $this->readConfig(); + } catch (Exception $e) { + $output->write($e->getMessage()); + + return 1; + } + + return $this->generateClassmapAutoloader(); + } + + /** + * Write a classmap to file iun each of the classmap_directory and dep_directory. + * + * Uses Composer's `ClassMapGenerator::createMap()` to scan the directories for classes and generate the map. + * + * createMap() returns the full local path, so we then replace the root of the path with a variable. + * + * @see ClassMapGenerator::dump() + * + */ + private function generateClassmapAutoloader(): int + { + + // Hyphen used to match WordPress Coding Standards. + $output_filename = "autoload-classmap.php"; + + $classmap_directory = getcwd() + . DIRECTORY_SEPARATOR + . ltrim($this->config->classmap_directory, DIRECTORY_SEPARATOR); + + $dep_directory = getcwd() + . DIRECTORY_SEPARATOR + . ltrim($this->config->dep_directory, DIRECTORY_SEPARATOR); + + $dirs = array( + $classmap_directory, + $dep_directory + ); + + foreach ($dirs as $dir) { + if (!is_dir($dir)) { + continue; + } + + $dirMap = ClassMapGenerator::createMap($dir); + + $dirname = preg_replace('/[^a-z]/i', '', str_replace(getcwd(), '', $dir)); + + array_walk( + $dirMap, + function (&$filepath, $class) use ($dir, $dirname) { + $filepath = "\${$dirname} . '" + . DIRECTORY_SEPARATOR + . ltrim(str_replace($dir, '', $filepath), DIRECTORY_SEPARATOR) . "'"; + } + ); + + ob_start(); + + echo " $file) { + echo " '{$class}' => {$file},\n"; + } + echo ");"; + + file_put_contents($dir . $output_filename, ob_get_clean()); + } + + return 0; + } +}