Loading core/lib/Drupal/Core/Recipe/Recipe.php +5 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ final class Recipe { public readonly string $name, public readonly string $description, public readonly string $type, public readonly RecipeConfigurator $recipes, public readonly InstallConfigurator $install, public readonly ConfigConfigurator $config, public readonly ContentConfigurator $content Loading @@ -39,6 +40,7 @@ final class Recipe { $recipe_data = Yaml::decode(file_get_contents($path . '/recipe.yml')) + [ 'description' => '', 'type' => '', 'recipes' => [], 'install' => [], 'config' => [], 'content' => [], Loading @@ -48,10 +50,12 @@ final class Recipe { throw new RecipeFileException("The $path/recipe.yml has no name value."); } $recipe_discovery = new RecipeDiscovery([dirname($path)]); $recipes = new RecipeConfigurator($recipe_data['recipes'], $recipe_discovery); $install = new InstallConfigurator($recipe_data['install'], \Drupal::service('extension.list.module'), \Drupal::service('extension.list.theme')); $config = new ConfigConfigurator($recipe_data['config'], $path, \Drupal::service('config.storage')); $content = new ContentConfigurator($recipe_data['content']); return new static($recipe_data['name'], $recipe_data['description'], $recipe_data['type'], $install, $config, $content); return new static($recipe_data['name'], $recipe_data['description'], $recipe_data['type'], $recipes, $install, $config, $content); } } core/lib/Drupal/Core/Recipe/RecipeConfigurator.php 0 → 100644 +24 −0 Original line number Diff line number Diff line <?php namespace Drupal\Core\Recipe; /** * @internal * This API is experimental. */ final class RecipeConfigurator { public readonly array $recipes; /** * @param string[] $recipes * A list of recipes for a recipe to apply. The recipes will be applied in * the order listed. * @param \Drupal\Core\Recipe\RecipeDiscovery $recipeDiscovery * Recipe discovery. */ public function __construct(array $recipes, RecipeDiscovery $recipeDiscovery) { $this->recipes = array_map([$recipeDiscovery, 'getRecipe'], $recipes); } } core/lib/Drupal/Core/Recipe/RecipeDiscovery.php 0 → 100644 +46 −0 Original line number Diff line number Diff line <?php namespace Drupal\Core\Recipe; use Drupal\Component\Assertion\Inspector; /** * @internal * This API is experimental. */ final class RecipeDiscovery { /** * Constructs a recipe discovery object. * * @param array $paths * An array of paths where to search for recipes. The path will be searched * folders containing a recipe.yml. There will be no traversal further into * the directory structure. */ public function __construct(protected readonly array $paths) { assert(Inspector::assertAllStrings($paths), 'Paths must be strings.'); } /** * Constructs a RecipeDiscovery object. * * @param string $name * The machine name of the recipe to find. * * @return \Drupal\Core\Recipe\Recipe * The recipe object. * * @throws \Drupal\Core\Recipe\UnknownRecipeException * Thrown when the recipe cannot be found. */ public function getRecipe(string $name): Recipe { foreach ($this->paths as $path) { if (file_exists($path . DIRECTORY_SEPARATOR . $name . DIRECTORY_SEPARATOR . 'recipe.yml')) { return Recipe::createFromDirectory($path . DIRECTORY_SEPARATOR . $name); } } throw new UnknownRecipeException($name, $this->paths, sprintf("Can not find the %s recipe, search paths: %s", $name, implode(', ', $this->paths))); } } core/lib/Drupal/Core/Recipe/RecipeRunner.php +13 −0 Original line number Diff line number Diff line Loading @@ -23,11 +23,24 @@ final class RecipeRunner { * The recipe to apply. */ public static function processRecipe(Recipe $recipe): void { static::processRecipes($recipe->recipes); static::processInstall($recipe->install, $recipe->config->getConfigStorage()); static::processConfiguration($recipe->config); static::processContent($recipe->content); } /** * Applies any recipes listed by the recipe. * * @param \Drupal\Core\Recipe\RecipeConfigurator $recipes * The list of recipes to apply. */ protected static function processRecipes(RecipeConfigurator $recipes): void { foreach ($recipes->recipes as $recipe) { static::processRecipe($recipe); } } /** * Installs the extensions. * Loading core/lib/Drupal/Core/Recipe/UnknownRecipeException.php 0 → 100644 +29 −0 Original line number Diff line number Diff line <?php namespace Drupal\Core\Recipe; /** * Exception thrown when recipe is can not be found. * * @internal * This API is experimental. */ final class UnknownRecipeException extends \RuntimeException { /** * @param string $recipe * The recipe's name. * @param array $searchPaths * The paths searched for the recipe. * @param string $message * (optional) The exception message. * @param int $code * (optional) The exception code. * @param \Throwable|null $previous * (optional) The previous exception. */ public function __construct(public readonly string $recipe, public readonly array $searchPaths, string $message = "", int $code = 0, ?\Throwable $previous = NULL) { parent::__construct($message, $code, $previous); } } Loading
core/lib/Drupal/Core/Recipe/Recipe.php +5 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ final class Recipe { public readonly string $name, public readonly string $description, public readonly string $type, public readonly RecipeConfigurator $recipes, public readonly InstallConfigurator $install, public readonly ConfigConfigurator $config, public readonly ContentConfigurator $content Loading @@ -39,6 +40,7 @@ final class Recipe { $recipe_data = Yaml::decode(file_get_contents($path . '/recipe.yml')) + [ 'description' => '', 'type' => '', 'recipes' => [], 'install' => [], 'config' => [], 'content' => [], Loading @@ -48,10 +50,12 @@ final class Recipe { throw new RecipeFileException("The $path/recipe.yml has no name value."); } $recipe_discovery = new RecipeDiscovery([dirname($path)]); $recipes = new RecipeConfigurator($recipe_data['recipes'], $recipe_discovery); $install = new InstallConfigurator($recipe_data['install'], \Drupal::service('extension.list.module'), \Drupal::service('extension.list.theme')); $config = new ConfigConfigurator($recipe_data['config'], $path, \Drupal::service('config.storage')); $content = new ContentConfigurator($recipe_data['content']); return new static($recipe_data['name'], $recipe_data['description'], $recipe_data['type'], $install, $config, $content); return new static($recipe_data['name'], $recipe_data['description'], $recipe_data['type'], $recipes, $install, $config, $content); } }
core/lib/Drupal/Core/Recipe/RecipeConfigurator.php 0 → 100644 +24 −0 Original line number Diff line number Diff line <?php namespace Drupal\Core\Recipe; /** * @internal * This API is experimental. */ final class RecipeConfigurator { public readonly array $recipes; /** * @param string[] $recipes * A list of recipes for a recipe to apply. The recipes will be applied in * the order listed. * @param \Drupal\Core\Recipe\RecipeDiscovery $recipeDiscovery * Recipe discovery. */ public function __construct(array $recipes, RecipeDiscovery $recipeDiscovery) { $this->recipes = array_map([$recipeDiscovery, 'getRecipe'], $recipes); } }
core/lib/Drupal/Core/Recipe/RecipeDiscovery.php 0 → 100644 +46 −0 Original line number Diff line number Diff line <?php namespace Drupal\Core\Recipe; use Drupal\Component\Assertion\Inspector; /** * @internal * This API is experimental. */ final class RecipeDiscovery { /** * Constructs a recipe discovery object. * * @param array $paths * An array of paths where to search for recipes. The path will be searched * folders containing a recipe.yml. There will be no traversal further into * the directory structure. */ public function __construct(protected readonly array $paths) { assert(Inspector::assertAllStrings($paths), 'Paths must be strings.'); } /** * Constructs a RecipeDiscovery object. * * @param string $name * The machine name of the recipe to find. * * @return \Drupal\Core\Recipe\Recipe * The recipe object. * * @throws \Drupal\Core\Recipe\UnknownRecipeException * Thrown when the recipe cannot be found. */ public function getRecipe(string $name): Recipe { foreach ($this->paths as $path) { if (file_exists($path . DIRECTORY_SEPARATOR . $name . DIRECTORY_SEPARATOR . 'recipe.yml')) { return Recipe::createFromDirectory($path . DIRECTORY_SEPARATOR . $name); } } throw new UnknownRecipeException($name, $this->paths, sprintf("Can not find the %s recipe, search paths: %s", $name, implode(', ', $this->paths))); } }
core/lib/Drupal/Core/Recipe/RecipeRunner.php +13 −0 Original line number Diff line number Diff line Loading @@ -23,11 +23,24 @@ final class RecipeRunner { * The recipe to apply. */ public static function processRecipe(Recipe $recipe): void { static::processRecipes($recipe->recipes); static::processInstall($recipe->install, $recipe->config->getConfigStorage()); static::processConfiguration($recipe->config); static::processContent($recipe->content); } /** * Applies any recipes listed by the recipe. * * @param \Drupal\Core\Recipe\RecipeConfigurator $recipes * The list of recipes to apply. */ protected static function processRecipes(RecipeConfigurator $recipes): void { foreach ($recipes->recipes as $recipe) { static::processRecipe($recipe); } } /** * Installs the extensions. * Loading
core/lib/Drupal/Core/Recipe/UnknownRecipeException.php 0 → 100644 +29 −0 Original line number Diff line number Diff line <?php namespace Drupal\Core\Recipe; /** * Exception thrown when recipe is can not be found. * * @internal * This API is experimental. */ final class UnknownRecipeException extends \RuntimeException { /** * @param string $recipe * The recipe's name. * @param array $searchPaths * The paths searched for the recipe. * @param string $message * (optional) The exception message. * @param int $code * (optional) The exception code. * @param \Throwable|null $previous * (optional) The previous exception. */ public function __construct(public readonly string $recipe, public readonly array $searchPaths, string $message = "", int $code = 0, ?\Throwable $previous = NULL) { parent::__construct($message, $code, $previous); } }