diff --git a/CHANGELOG.md b/CHANGELOG.md index a33786324..49871fcb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,60 @@ All notable changes to this project will be documented in this file. This projects adheres to [Semantic Versioning](https://semver.org/) and [Keep a CHANGELOG](https://keepachangelog.com/). +## [8.0.0] - TBA + +### Changed +- Complete refactor of the block registration process for faster and more efficient block registration. +- Custom post type and taxonomy registration labels usage is now more consistent and simplified. +- All enqueue methods now supports new scripts args introduced in WP 6.3. +- All enqueue methods now implements `manifestCache`interface for fetching manifest data. +- All AbstractGeolocation method now implements `manifestCache`interface for fetching manifest data. + +### Added +- Internal caching in WordPress transients for faster manifest parsing and block registration. +- Blocks render method now uses the Helpers::render method for better performance and code readability. +- New ManifestCache CLI command with example. +- New CLI commands for config theme and plugin. +- - `Exception/InvalidBlock.php` method: `missingItemException`. +- `src/Exception/InvalidManifest.php` methods: `missingManifestKeyException`, `emptyOrErrorManifestException`, `notAllowedManifestPathException`, `notAllowedManifestPathItemException`, `missingCacheTopItemException`, `missingCacheSubItemException`. +- `src/Exception/InvalidPath.php` method: `missingDirectoryException`, `missingFileException`, `missingFileWithExampleException`, `wrongOrNotAllowedParentPathException`. +- New `src/Helpers/ApiTrait.php` helper for better API handling. +- New `recursiveArrayFind` helper for finding items in multidimensional arrays recursively. +- New casing helpers `camelToSnakeCase`, `kebabToSnakeCase`. +- New `getCurrentUrl` helper for getting the current URL with query parameters. +- New `cleanUrlParams` helper for cleaning URL parameters. +- New project info helpers: `getPluginVersion`, `getPluginName`, `getPluginTextDomain`, `getThemeVersion`, `getThemeName`, `getThemeTextDomain`, `getPluginDetails`. +- New constants for API handing in the `src/Rest/Routes/AbstractRoute.php`. + +### Fixed +- Typo in block enqueue method for `getBlockFrontentScriptHandle` to `getBlockFrontendScriptHandle` method. + +### Removed +- `src/Blocks/AbstractBlocks.php` method `renderWrapperView` is removed. +- `Config/AbstractConfigData.php` is removed as it is no longer needed. Use helpers instead. +- `Config/ConfigDataInterface.php` is removed as it is no longer needed. +- `LabelGeneratorTrait` is removed as it is no longer needed. +- All Enqueue methods no longer use `manifest` interface. +- `Exception/ComponentException.php` is removed as it is no longer needed. +- `Exception/FailedToLoadView.php` is removed as it is no longer needed. +- `Exception/FileMissing.php` is removed as it is no longer needed. +- `src/Exception/InvalidCallback.php` is removed as it is no longer needed. +- `src/Exception/InvalidNouns.php` is removed as it is no longer needed. +- `src/Exception/InvalidService.php` is removed as it is no longer needed. +- `src/Exception/PluginActivationFailure.php` is removed as it is no longer needed. +- `Exception/InvalidBlock.php` methods: `missingBlocksException`, `missingComponentsException`, `missingNameException`, `missingComponentNameException`, `missingVariationNameException`, `missingViewException`, `missingRenderViewException`, `missingSettingsManifestException`, `missingWrapperManifestException`, `missingComponentManifestException`, `missingWrapperViewException`, `missingNamespaceException`, `missingSettingsKeyException`, `wrongFunctionUsedException`, `missingFileException`, is removed as it is no longer needed. +- `src/Exception/InvalidManifest.php` method: `missingManifestItemException`. +- `src/Exception/InvalidPath.php` method: `fromUri`. +- `outputCssVariablesGlobal` method no longer requires parameters. +- `outputCssVariables` method is not longer supporting `globalManifest` parameter. +- all methods from `src/Helpers/ErrorLoggerTrait.php` trait are removed. +- All manifest classes are removed, `src/Manifest/AbstractManifest.php`, `src/Manifest/ManifestExample.php`, `src/Manifest/ManifestInterface.php`. + +### Deprecated +- `Helpers>Components` class is deprecated and will be removed in the next major release. Use `Helpers>Blocks` class instead. +- `Helpers>Components>renderPartial` method is deprecated and will be removed in the next major release. Use `Helpers>Helpers>render` method instead. +- `Helpers>Components>getManifest` method is deprecated and will be removed in the next major release. Use `Helpers>Helpers>getManifestByDir` method instead. + ## [7.1.2] - 2024-01-15 ### Changed @@ -489,6 +543,7 @@ Init setup [Unreleased]: https://github.com/infinum/eightshift-libs/compare/main...HEAD +[8.0.0]: https://github.com/infinum/eightshift-libs/compare/7.1.2...8.0.0 [7.1.2]: https://github.com/infinum/eightshift-libs/compare/7.1.1...7.1.2 [7.1.1]: https://github.com/infinum/eightshift-libs/compare/7.1.0...7.1.1 [7.1.0]: https://github.com/infinum/eightshift-libs/compare/7.0.1...7.1.0 diff --git a/src/Blocks/AbstractBlocks.php b/src/Blocks/AbstractBlocks.php index da35b2547..fd6a435c1 100644 --- a/src/Blocks/AbstractBlocks.php +++ b/src/Blocks/AbstractBlocks.php @@ -11,8 +11,10 @@ namespace EightshiftLibs\Blocks; +use EightshiftLibs\Cache\AbstractManifestCache; +use EightshiftLibs\Cache\ManifestCacheInterface; use EightshiftLibs\Exception\InvalidBlock; -use EightshiftLibs\Helpers\Components; +use EightshiftLibs\Helpers\Helpers; use EightshiftLibs\Services\ServiceInterface; use WP_Block_Editor_Context; use WP_Post; @@ -22,6 +24,23 @@ */ abstract class AbstractBlocks implements ServiceInterface, RenderableBlockInterface { + /** + * Instance variable for manifest cache. + * + * @var ManifestCacheInterface + */ + protected $manifestCache; + + /** + * Create a new instance. + * + * @param ManifestCacheInterface $manifestCache Inject manifest cache. + */ + public function __construct(ManifestCacheInterface $manifestCache) + { + $this->manifestCache = $manifestCache; + } + /** * Create custom project color palette. * These colors are fetched from the main settings manifest.json. @@ -31,7 +50,7 @@ abstract class AbstractBlocks implements ServiceInterface, RenderableBlockInterf public function changeEditorColorPalette(): void { // Unable to use state due to this method is used in JS and store is not registered there. - $colors = $this->getSettingsManifest()['globalVariables']['colors'] ?? []; + $colors = $this->manifestCache->getManifestCacheTopItem(AbstractManifestCache::SETTINGS_KEY)['globalVariables']['colors'] ?? []; if ($colors) { \add_theme_support('editor-color-palette', $colors); @@ -57,33 +76,25 @@ public function addThemeSupport(): void */ public function getBlocksDataFullRaw(): void { - // Get global settings direct from file. - $settings = $this->getSettingsManifest(); + // Register store and set all the data. + Helpers::setStore(); + Helpers::setSettings($this->manifestCache->getManifestCacheTopItem(AbstractManifestCache::SETTINGS_KEY)); + Helpers::setConfigFlags(); - $namespace = $settings['namespace']; + if (Helpers::getConfigUseBlocks()) { + Helpers::setBlocks($this->manifestCache->getManifestCacheTopItem(AbstractManifestCache::BLOCKS_KEY)); + } - $blocks = \array_map( - static function ($block) use ($namespace) { - // Check if blocks-namespace is defined in block or in global manifest settings. - $block['namespace'] = $namespace; - $block['blockFullName'] = "{$namespace}/{$block['blockName']}"; + if (Helpers::getConfigUseComponents()) { + Helpers::setComponents($this->manifestCache->getManifestCacheTopItem(AbstractManifestCache::COMPONENTS_KEY)); + } - return $block; - }, - $this->getBlocksManifests() - ); + if (Helpers::getConfigUseVariations()) { + Helpers::setVariations($this->manifestCache->getManifestCacheTopItem(AbstractManifestCache::VARIATIONS_KEY)); + } - // Register store and set all the data. - Components::setStore(); - Components::setSettings($settings); - Components::setBlocks($blocks); - Components::setComponents($this->getComponentsManifests()); - Components::setVariations($this->getVariationsManifests()); - Components::setConfigFlags(); - Components::setPaths(); - - if (Components::getConfigUseWrapper()) { - Components::setWrapper($this->getWrapperManifest()); + if (Helpers::getConfigUseWrapper()) { + Helpers::setWrapper($this->manifestCache->getManifestCacheTopItem(AbstractManifestCache::WRAPPER_KEY)); } } @@ -116,15 +127,15 @@ public function getAllAllowedBlocksList($allowedBlockTypes, WP_Block_Editor_Cont return $allowedBlockTypes; } - $allowedBlockTypes = \array_merge( - \array_map( - function ($block) { - return $block['blockFullName']; - }, - Components::getBlocks(), - ), - $allowedBlockTypes, - ); + if (Helpers::getConfigUseBlocks()) { + $allowedBlockTypes = \array_merge( + \array_map( + fn ($block) => $block['blockFullName'], + $this->manifestCache->getManifestCacheTopItem(AbstractManifestCache::BLOCKS_KEY) + ), + $allowedBlockTypes, + ); + } // Allow reusable block. $allowedBlockTypes[] = 'eightshift-forms/forms'; @@ -156,13 +167,15 @@ public function getAllBlocksList($allowedBlockTypes, WP_Block_Editor_Context $bl /** * Method used to register all custom blocks with data fetched from blocks manifest.json. * - * @throws InvalidBlock Throws error if blocks are missing. - * * @return void */ public function registerBlocks(): void { - foreach (Components::getBlocks() as $block) { + if (!Helpers::getConfigUseBlocks()) { + return; + } + + foreach ($this->manifestCache->getManifestCacheTopItem(AbstractManifestCache::BLOCKS_KEY) as $block) { $this->registerBlock($block); } } @@ -173,46 +186,35 @@ public function registerBlocks(): void * @param array $attributes Array of attributes as defined in block's manifest.json. * @param string $innerBlockContent Block's content if using inner blocks. * - * @throws InvalidBlock Throws error if block view is missing. - * * @return string Html template for block. */ public function render(array $attributes, string $innerBlockContent): string { - // Block details is unavailable in this method so we are fetching block name via attributes. - $blockName = $attributes['blockName'] ?? ''; - // Get block view path. - $sep = \DIRECTORY_SEPARATOR; - $templatePath = Components::getProjectPaths('blocksDestinationCustom', "{$blockName}{$sep}{$blockName}.php"); + $blockOutpout = Helpers::render( + $attributes['blockName'] ?? '', + $attributes, + 'blocks', + false, + '', + $innerBlockContent + ); // Get block wrapper view path. - if (Components::getConfigUseWrapper()) { - $wrapperPath = Components::getProjectPaths('blocksDestinationWrapper', 'wrapper.php'); - - // Check if wrapper component exists. - if (!\file_exists($wrapperPath)) { - throw InvalidBlock::missingWrapperViewException($wrapperPath); - } - - // Check if actual block exists. - if (!\file_exists($templatePath)) { - throw InvalidBlock::missingViewException($blockName, $templatePath); - } - - // If everything is ok, return the contents of the template (return, NOT echo). - \ob_start(); - include $wrapperPath; - $output = \ob_get_clean(); + if (Helpers::getConfigUseWrapper()) { + echo Helpers::render( // phpcs:ignore Eightshift.Security.ComponentsEscape.OutputNotEscaped + 'wrapper', + $attributes, + 'wrapper', + false, + '', + $blockOutpout + ); } else { - \ob_start(); - include $templatePath; - $output = \ob_get_clean(); + echo $blockOutpout; // phpcs:ignore Eightshift.Security.ComponentsEscape.OutputNotEscaped } - unset($blockName, $templatePath, $wrapperPath, $attributes, $innerBlockContent); - - return (string)$output; + return ''; } /** @@ -241,30 +243,6 @@ public function getCustomCategory(array $categories, WP_Block_Editor_Context $bl ); } - /** - * Locate and return template part with passed attributes for wrapper. - * - * Used to render php block wrapper view. - * - * @param string $src String with URL path to template. - * @param array $attributes Attributes array to pass in template. - * @param string|null $innerBlockContent If using inner blocks content pass the data. - * - * @throws InvalidBlock Throws an error if wrapper file doesn't exist. - * - * @return void Includes an HTML view, or throws an error if the view is missing. - */ - public function renderWrapperView(string $src, array $attributes, ?string $innerBlockContent = null): void - { - if (!\file_exists($src)) { - throw InvalidBlock::missingWrapperViewException($src); - } - - include $src; - - unset($src, $attributes, $innerBlockContent); - } - /** * Removes paragraph block from the php part if the content is empty. * @@ -277,7 +255,7 @@ public function renderWrapperView(string $src, array $attributes, ?string $inner */ public function filterBlocksContent(array $parsedBlock, array $sourceBlock): array { - $namespace = Components::getSettingsNamespace(); + $namespace = Helpers::getSettingsNamespace(); if ($parsedBlock['blockName'] === "{$namespace}/paragraph") { $content = $parsedBlock['attrs']['paragraphParagraphContent'] ?? ''; @@ -298,7 +276,7 @@ public function filterBlocksContent(array $parsedBlock, array $sourceBlock): arr */ public function outputCssVariablesInline(): void { - echo Components::outputCssVariablesInline(); // phpcs:ignore + echo Helpers::outputCssVariablesInline(); // phpcs:ignore } /** @@ -321,149 +299,6 @@ private function registerBlock(array $blockDetails): void ); } - /** - * Retrieve block data from manifest.json combined with some additional stuff. - * - * @throws InvalidBlock Throws error if block name is missing. - * - * @return array> - */ - private function getBlocksManifests(): array - { - $sep = \DIRECTORY_SEPARATOR; - $path = Components::getProjectPaths('blocksDestinationCustom'); - - return \array_map( - function (string $blockPath) { - $block = \implode(' ', (array)\file(($blockPath))); - - $block = Components::parseManifest($block); - - if (!isset($block['blockName'])) { - throw InvalidBlock::missingNameException($blockPath); - } - - if (!isset($block['classes'])) { - $block['classes'] = []; - } - - if (!isset($block['attributes'])) { - $block['attributes'] = []; - } - - if (!isset($block['hasInnerBlocks'])) { - $block['hasInnerBlocks'] = false; - } - - return $block; - }, - (array)\glob("{$path}*{$sep}manifest.json") - ); - } - - /** - * Retrieve components data from manifest.json. - * - * @throws InvalidBlock Throws error if component name is missing. - * - * @return array> - */ - private function getComponentsManifests(): array - { - $sep = \DIRECTORY_SEPARATOR; - $path = Components::getProjectPaths('blocksDestinationComponents'); - - return \array_map( - function (string $componentPath) { - $component = \implode(' ', (array)\file(($componentPath))); - - $component = Components::parseManifest($component); - - if (!isset($component['componentName'])) { - throw InvalidBlock::missingComponentNameException($componentPath); - } - - return $component; - }, - (array)\glob("{$path}*{$sep}manifest.json") - ); - } - - /** - * Retrieve variations data from manifest.json. - * - * @throws InvalidBlock Throws error if variation name is missing. - * - * @return array> - */ - private function getVariationsManifests(): array - { - $sep = \DIRECTORY_SEPARATOR; - $path = Components::getProjectPaths('blocksDestinationVariations'); - - return \array_map( - function (string $variationPath) { - $variation = \implode(' ', (array)\file(($variationPath))); - - $variation = Components::parseManifest($variation); - - if (!isset($variation['name'])) { - throw InvalidBlock::missingVariationNameException($variationPath); - } - - return $variation; - }, - (array)\glob("{$path}*{$sep}manifest.json") - ); - } - - /** - * Retrieve wrapper data from manifest.json. - * - * @throws InvalidBlock Throws error if wrapper settings manifest.json is missing. - * - * @return array - */ - private function getWrapperManifest(): array - { - $path = Components::getProjectPaths('blocksDestinationWrapper', "manifest.json"); - - if (!\file_exists($path)) { - throw InvalidBlock::missingWrapperManifestException($path); - } - - $manifest = \implode(' ', (array)\file($path)); - $manifest = Components::parseManifest($manifest); - - return $manifest; - } - - /** - * Get blocks global settings manifest data from settings manifest.json file. - * - * @throws InvalidBlock Throws error if global manifest settings key block-namespace is missing. - * @throws InvalidBlock Throws error if global settings manifest.json is missing. - * - * @return array - */ - private function getSettingsManifest(): array - { - $path = Components::getProjectPaths('blocksDestination', 'manifest.json'); - - if (!\file_exists($path)) { - throw InvalidBlock::missingSettingsManifestException($path); - } - - $manifest = \implode(' ', (array)\file(($path))); - $manifest = Components::parseManifest($manifest); - - if (!isset($manifest['namespace'])) { - throw InvalidBlock::missingNamespaceException(); - } - - return $manifest; - } - /** * Prepare all blocks attributes. * @@ -479,12 +314,12 @@ private function getSettingsManifest(): array private function getAttributes(array $blockDetails): array { $blockName = $blockDetails['blockName']; - $blockClassPrefix = Components::getSettingsBlockClassPrefix(); + $blockClassPrefix = Helpers::getSettingsBlockClassPrefix(); $wrapperAttributes = []; - if (Components::getConfigUseWrapper()) { - $wrapperAttributes = Components::getWrapperAttributes(); + if (Helpers::getConfigUseWrapper()) { + $wrapperAttributes = Helpers::getWrapperAttributes(); } return \array_merge( @@ -499,7 +334,7 @@ private function getAttributes(array $blockDetails): array ], 'blockTopLevelId' => [ 'type' => 'string', - 'default' => Components::getUnique(), + 'default' => Helpers::getUnique(), ], 'blockFullName' => [ 'type' => 'string', @@ -518,7 +353,7 @@ private function getAttributes(array $blockDetails): array 'default' => false, ], ], - Components::getSettingsAttributes(), + Helpers::getSettingsAttributes(), $wrapperAttributes, $this->prepareComponentAttributes($blockDetails) ); @@ -548,7 +383,7 @@ private function prepareComponentAttribute(array $manifest, string $newName, str } // Make sure the case is always correct for parent. - $newParent = Components::kebabToCamelCase($parent); + $newParent = Helpers::kebabToCamelCase($parent); // Iterate each attribute and attach parent prefixes. $componentAttributeKeys = \array_keys($componentAttributes); @@ -562,7 +397,7 @@ private function prepareComponentAttribute(array $manifest, string $newName, str // Check if current attribute is used strip component prefix from attribute and replace it with parent prefix. if ($currentAttributes) { - $attribute = \str_replace(\lcfirst(Components::kebabToCamelCase($realName)), '', (string) $componentAttribute); + $attribute = \str_replace(\lcfirst(Helpers::kebabToCamelCase($realName)), '', (string) $componentAttribute); } // Determine if parent is empty and if parent name is the same as component/block name and skip wrapper attributes. @@ -604,7 +439,7 @@ private function prepareComponentAttributes(array $manifest, string $parent = '' // Iterate over components key in manifest recursively and check component names. foreach ($components as $newComponentName => $realComponentName) { // Filter components real name. - $component = Components::getComponent(Components::camelToKebabCase($realComponentName)); + $component = Helpers::getComponent(Helpers::camelToKebabCase($realComponentName)); // Bailout if component doesn't exist. if (!$component) { @@ -613,7 +448,7 @@ private function prepareComponentAttributes(array $manifest, string $parent = '' // If component has more components do recursive loop. if (isset($component['components'])) { - $outputAttributes = $this->prepareComponentAttributes($component, $newParent . \ucfirst(Components::camelToKebabCase($newComponentName))); + $outputAttributes = $this->prepareComponentAttributes($component, $newParent . \ucfirst(Helpers::camelToKebabCase($newComponentName))); } else { // Output the component attributes if there is no nesting left, and append the parent prefixes. $outputAttributes = $this->prepareComponentAttribute($component, $newComponentName, $realComponentName, $newParent); diff --git a/src/Blocks/AbstractBlocksCli.php b/src/Blocks/AbstractBlocksCli.php index 233e7cc91..222b0964a 100644 --- a/src/Blocks/AbstractBlocksCli.php +++ b/src/Blocks/AbstractBlocksCli.php @@ -221,7 +221,7 @@ static function ($item) use ($sep) { */ private function outputDependencyItems(string $source, string $type): void { - $manifest = Components::getManifestDirect($source); + $manifest = $this->getManifestDirect($source); // Component dependency. $componentsDependencies = $manifest['components'] ?? []; @@ -272,7 +272,7 @@ private function outputDependencyItems(string $source, string $type): void */ private function outputNodeModuleDependencyItems(string $source, string $type): void { - $manifest = Components::getManifestDirect($source); + $manifest = $this->getManifestDirect($source); // Node_module dependency. $nodeDependencies = $manifest['nodeDependency'] ?? []; diff --git a/src/Cache/AbstractManifestCache.php b/src/Cache/AbstractManifestCache.php new file mode 100644 index 000000000..b9070b0e6 --- /dev/null +++ b/src/Cache/AbstractManifestCache.php @@ -0,0 +1,531 @@ + Array of cache item. + */ + public function getManifestCacheTopItem(string $key, string $cacheType = self::TYPE_BLOCKS): array + { + $output = []; + + if (\defined('WP_ENVIRONMENT_TYPE') && \WP_ENVIRONMENT_TYPE !== 'development') { + $output = $this->getCache($cacheType)[$key] ?? []; + } + + if (!$output) { + $output = $this->getAllManifests($cacheType)[$key] ?? []; + } + + if (!$output) { + throw InvalidManifest::missingCacheTopItemException($key, $this->getFullPath($key, $cacheType)); + } + + return $output; + } + + /** + * Get manifest cache subitem. + * + * @param string $key Key of the cache. + * @param string $name Name of the subitem. + * @param string $cacheType Type of the cache. + * + * @throws InvalidManifest If cache subitem is missing. + * + * @return array Array of cache item. + */ + public function getManifestCacheSubItem(string $key, string $name, string $cacheType = self::TYPE_BLOCKS): array + { + $output = $this->getManifestCacheTopItem($key, $cacheType)[$name] ?? []; + + if (!$output) { + throw InvalidManifest::missingCacheSubItemException($key, $name, $this->getFullPath($key, $cacheType, $name)); + } + + return $output; + } + + /** + * Set all cache. + * + * @param array $ignoreCache Array of cache to ignore. + * + * @return void + */ + public function setAllCache($ignoreCache = []): void + { + $ignoreCache = \array_flip($ignoreCache); + + if (!isset($ignoreCache[self::TYPE_BLOCKS])) { + $this->setCache(self::TYPE_BLOCKS); + } + + if (!isset($ignoreCache[self::TYPE_ASSETS])) { + $this->setCache(self::TYPE_ASSETS); + $this->setAssetsStaticCache(); + } + + if (!isset($ignoreCache[self::TYPE_GEOLOCATION])) { + $this->setCache(self::TYPE_GEOLOCATION); + } + } + + /** + * Set assets static cache to the store helpers used in view files. + * + * @return void + */ + protected function setAssetsStaticCache(): void + { + Helpers::setStore(); + Helpers::setAssets($this->getManifestCacheTopItem(self::ASSETS_KEY, self::TYPE_ASSETS)); + } + + /** + * Set cache. + * + * @param string $cacheType Type of the cache. + * + * @return void + */ + protected function setCache(string $cacheType = self::TYPE_BLOCKS): void + { + $name = self::TRANSIENT_NAME . $this->getCacheName() . "_{$cacheType}"; + + $cache = \get_transient($name); + + if (!$cache) { + \set_transient($name, \wp_json_encode($this->getAllManifests($cacheType)), $this->getDuration()); + } + } + + /** + * Get cache. + * + * @param string $cacheType Type of the cache. + * + * @return array> Array of cache. + */ + protected function getCache(string $cacheType = self::TYPE_BLOCKS): array + { + $cache = \get_transient(self::TRANSIENT_NAME . $this->getCacheName() . "_{$cacheType}"); + + if (!$cache) { + $this->setCache(); + } + + return \json_decode($cache, true) ?? []; + } + + /** + * Unset cache. + * + * @param string $cacheType Type of the cache. + * + * @return void + */ + protected function deleteCache(string $cacheType = self::TYPE_BLOCKS): void + { + \delete_transient(self::TRANSIENT_NAME . $this->getCacheName() . "_{$cacheType}"); + } + + /** + * Get cache builder. + * + * @return array> Array of cache builder. + */ + protected function getCacheBuilder(): array + { + $sep = \DIRECTORY_SEPARATOR; + + return [ + self::TYPE_BLOCKS => [ + self::SETTINGS_KEY => [ + 'path' => 'blocksDestination', + 'multiple' => false, + 'validation' => [ + '$schema', + 'namespace', + 'background', + 'foreground', + ], + ], + self::BLOCKS_KEY => [ + 'path' => 'blocksDestinationCustom', + 'id' => 'blockName', + 'multiple' => true, + 'autoset' => [ + 'classes' => 'array', + 'attributes' => 'array', + 'hasInnerBlocks' => 'boolean', + ], + 'validation' => [ + '$schema', + 'title', + 'description', + 'namespace', + 'blockName', + 'blockFullName', + 'keywords', + 'icon', + 'category', + ], + ], + self::COMPONENTS_KEY => [ + 'path' => 'blocksDestinationComponents', + 'multiple' => true, + 'id' => 'componentName', + 'validation' => [ + '$schema', + 'title', + 'componentName', + ], + ], + self::VARIATIONS_KEY => [ + 'path' => 'blocksDestinationVariations', + 'id' => 'name', + 'multiple' => true, + 'validation' => [ + '$schema', + 'title', + 'description', + 'icon', + 'name', + 'parentName', + ], + ], + self::WRAPPER_KEY => [ + 'path' => 'blocksDestinationWrapper', + 'validation' => [ + '$schema', + 'title', + ], + ], + ], + self::TYPE_ASSETS => [ + self::ASSETS_KEY => [ + 'path' => 'themeRoot', + 'fileName' => "public{$sep}manifest.json", + ], + ], + self::TYPE_GEOLOCATION => [ + self::COUNTRIES_KEY => [ + 'path' => 'libs', + 'pathAlternative' => 'libsPrefixed', + 'fileName' => "src{$sep}Geolocation{$sep}manifest.json", + ], + ], + ]; + } + + /** + * Get all manifests from the paths. + * + * @param string $cacheType Type of the cache. + * + * @return array> Array of manifests. + */ + private function getAllManifests(string $cacheType = self::TYPE_BLOCKS): array + { + $output = []; + + foreach ($this->getCacheBuilder()[$cacheType] ?? [] as $parent => $data) { + $multiple = $data['multiple'] ?? false; + + if ($multiple) { + $output[$parent] = $this->geItems($this->getFullPath($parent, $cacheType, '*'), $data, $parent); + } else { + $output[$parent] = $this->getItem($this->getFullPath($parent, $cacheType), $data, $parent); + } + } + + return $output; + } + + /** + * Get single item from the path. + * + * @param string $path Path to the item. + * @param array $data Data array. + * @param string $parent Parent key. + * + * @throws InvalidManifest If manifest key is missing. + * + * @return array Item. + */ + private function getItem(string $path, array $data, string $parent): array + { + if (!\file_exists($path)) { + return []; + } + + $file = \file_get_contents($path); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents + + if (!$file) { + return []; + } + + $fileDecoded = \json_decode($file, true); + + if (!$fileDecoded) { + return []; + } + + $autoset = $data['autoset'] ?? []; + + if ($autoset) { + foreach ($autoset as $key => $type) { + if (isset($fileDecoded[$key])) { + continue; + } + + switch ($type) { + case 'array': + $fileDecoded[$key] = []; + break; + case 'boolean': + $fileDecoded[$key] = false; + break; + default: + $fileDecoded[$key] = ''; + break; + } + } + } + + switch ($parent) { + case self::BLOCKS_KEY: + if ($this->blocksNamespace) { + $fileDecoded['namespace'] = $this->blocksNamespace; + $fileDecoded['blockFullName'] = "{$this->blocksNamespace}/{$fileDecoded['blockName']}"; + } + break; + case self::SETTINGS_KEY: + $this->blocksNamespace = $fileDecoded['namespace'] ?? ''; + break; + } + + $validation = $data['validation'] ?? []; + + if ($validation) { + foreach ($validation as $key) { + if (!isset($fileDecoded[$key])) { + throw InvalidManifest::missingManifestKeyException($key, $path); + } + } + } + + return $fileDecoded; + } + + /** + * Get multiple items from the path. + * + * @param string $path Path to the items. + * @param array $data Data array. + * @param string $parent Parent key. + * + * @return array> Array of items. + */ + private function geItems(string $path, array $data, string $parent): array + { + $output = []; + + $id = $data['id'] ?? ''; + + foreach ((array)\glob($path) as $itemPath) { + $item = $this->getItem($itemPath, $data, $parent); + + $idName = $item[$id] ?? ''; + + if (!$idName) { + continue; + } + + $output[$idName] = $item; + } + + return $output; + } + + /** + * Get full path. + * + * @param string $type Type of the item. + * @param string $cacheType Type of the cache. + * @param string $name Name of the item. + * + * @return string Full path. + */ + private function getFullPath($type, string $cacheType = self::TYPE_BLOCKS, $name = ''): string + { + $data = $this->getCacheBuilder()[$cacheType][$type] ?? []; + + if (!$data) { + return ''; + } + + $path = $data['path'] ?? ''; + $pathAlternative = $data['pathAlternative'] ?? ''; + $pathCustom = $data['pathCustom'] ?? ''; + $fileName = $data['fileName'] ?? 'manifest.json'; + + $realPath = Helpers::getProjectPaths($path); + + if (!\is_dir($realPath) && $pathAlternative) { + $realPath = Helpers::getProjectPaths($pathAlternative); + } + + if ($pathCustom) { + $realPath = $pathCustom; + } + + if (!$name) { + return Helpers::joinPaths([$realPath, $fileName]); + } + + return Helpers::joinPaths([$realPath, $name, $fileName]); + } +} diff --git a/src/Manifest/ManifestCli.php b/src/Cache/ManifestCacheCli.php similarity index 70% rename from src/Manifest/ManifestCli.php rename to src/Cache/ManifestCacheCli.php index b7e94a719..2ad3670d9 100644 --- a/src/Manifest/ManifestCli.php +++ b/src/Cache/ManifestCacheCli.php @@ -1,23 +1,23 @@ 'Create manifest service class.', + 'shortdesc' => 'Create manifest cache service class.', 'longdesc' => $this->prepareLongDesc(" ## USAGE - Used to create manifest service class to register assets manifest functionality like getting fonts from your project. + Used to create manifest cache service class to optimize the loading of assets in your theme or plugin. ## EXAMPLES @@ -61,7 +61,7 @@ public function getDoc(): array ## RESOURCES Service class will be created from this example: - https://github.com/infinum/eightshift-libs/blob/develop/src/Manifest/ManifestExample.php + https://github.com/infinum/eightshift-libs/blob/develop/src/Cache/ManifestCacheExample.php "), ]; } @@ -78,6 +78,6 @@ public function __invoke(array $args, array $assocArgs) ->renameClassName($className) ->renameNamespace($assocArgs) ->renameUse($assocArgs) - ->outputWrite(Components::getProjectPaths('srcDestination', 'Manifest'), "{$className}.php", $assocArgs); + ->outputWrite(Helpers::getProjectPaths('srcDestination', 'Cache'), "{$className}.php", $assocArgs); } } diff --git a/src/Cache/ManifestCacheExample.php b/src/Cache/ManifestCacheExample.php new file mode 100644 index 000000000..5a4033a2c --- /dev/null +++ b/src/Cache/ManifestCacheExample.php @@ -0,0 +1,30 @@ + Array of cache item. + */ + public function getManifestCacheTopItem(string $key, string $cacheType = AbstractManifestCache::TYPE_BLOCKS): array; + + /** + * Get manifest cache subitem. + * + * @param string $key Key of the cache. + * @param string $path Path of the cache. + * @param string $cacheType Type of the cache. + * + * @return array Array of cache item. + */ + public function getManifestCacheSubItem(string $key, string $path, string $cacheType = AbstractManifestCache::TYPE_BLOCKS): array; +} diff --git a/src/Cli/AbstractCli.php b/src/Cli/AbstractCli.php index 3b61d30fd..13b79a344 100644 --- a/src/Cli/AbstractCli.php +++ b/src/Cli/AbstractCli.php @@ -10,7 +10,7 @@ namespace EightshiftLibs\Cli; -use EightshiftLibs\Exception\InvalidBlock; +use EightshiftLibs\Exception\InvalidPath; use EightshiftLibs\Helpers\Components; use FilesystemIterator; use RecursiveDirectoryIterator; @@ -924,7 +924,7 @@ public function prepareArgsManual(array $args): string * @param string $source Source path. * @param string $destination Destination path. * - * @throws InvalidBlock If block file is missing. + * @throws InvalidPath Exception in case the source path is missing. * * @return void */ @@ -940,7 +940,7 @@ protected function copyRecursively(string $source, string $destination): void RecursiveIteratorIterator::SELF_FIRST ); } catch (UnexpectedValueException $exception) { - throw InvalidBlock::missingFileException($source); + throw InvalidPath::missingFileException($source); } $ds = \DIRECTORY_SEPARATOR; @@ -1004,4 +1004,28 @@ protected function getIntroText(array $arg = []): void ╰──────────────────────────────────────────────────────────╯%n "), 'mixed'); } + + /** + * Get manifest json. Generally used for getting block/components manifest. Used to directly fetch json file. + * Used in combination with getManifest helper. + * + * @param string $path Absolute path to manifest folder. + * + * @throws InvalidPath Exception in case the manifest file is missing. + * + * @return array + */ + public function getManifestDirect(string $path): array + { + $sep = \DIRECTORY_SEPARATOR; + $path = \rtrim($path, $sep); + + $manifest = "{$path}{$sep}manifest.json"; + + if (!\file_exists($manifest)) { + throw InvalidPath::missingFileException($manifest); + } + + return \json_decode(\implode(' ', (array)\file($manifest)), true); + } } diff --git a/src/Cli/Cli.php b/src/Cli/Cli.php index be37505d9..40bf99ef1 100644 --- a/src/Cli/Cli.php +++ b/src/Cli/Cli.php @@ -24,13 +24,15 @@ use EightshiftLibs\Blocks\UseManifestCli; use EightshiftLibs\Blocks\UseVariationCli; use EightshiftLibs\Blocks\UseWrapperCli; +use EightshiftLibs\Cache\ManifestCacheCli; use EightshiftLibs\Cli\ParentGroups\CliBoilerplate; use EightshiftLibs\Cli\ParentGroups\CliCreate; use EightshiftLibs\Cli\ParentGroups\CliRun; use EightshiftLibs\Cli\ParentGroups\CliBlocks; use EightshiftLibs\Cli\ParentGroups\CliInit; use EightshiftLibs\Columns\Media\WebPMediaColumnCli; -use EightshiftLibs\Config\ConfigCli; +use EightshiftLibs\Config\ConfigThemeCli; +use EightshiftLibs\Config\ConfigPluginCli; use EightshiftLibs\ConfigProject\ConfigProjectCli; use EightshiftLibs\Setup\PluginManageCli; use EightshiftLibs\View\EscapedViewCli; @@ -45,7 +47,6 @@ use EightshiftLibs\I18n\I18nCli; use EightshiftLibs\Login\LoginCli; use EightshiftLibs\Main\MainCli; -use EightshiftLibs\Manifest\ManifestCli; use EightshiftLibs\Media\MediaCli; use EightshiftLibs\Menu\MenuCli; use EightshiftLibs\ModifyAdminAppearance\ModifyAdminAppearanceCli; @@ -102,7 +103,8 @@ class Cli ReusableBlocksHeaderFooterCli::class, AnalyticsGdprCli::class, WebPMediaColumnCli::class, - ConfigCli::class, + ConfigThemeCli::class, + ConfigPluginCli::class, ConfigProjectCli::class, AcfMetaCli::class, PostTypeCli::class, @@ -115,7 +117,6 @@ class Cli I18nCli::class, LoginCli::class, MainCli::class, - ManifestCli::class, MediaCli::class, MenuCli::class, ModifyAdminAppearanceCli::class, @@ -128,6 +129,7 @@ class Cli ThemeOptionsCli::class, EscapedViewCli::class, WpCli::class, + ManifestCacheCli::class, ]; /** diff --git a/src/Config/AbstractConfigData.php b/src/Config/AbstractConfigData.php deleted file mode 100644 index 5a4075224..000000000 --- a/src/Config/AbstractConfigData.php +++ /dev/null @@ -1,52 +0,0 @@ - '1', - ]; + return []; } /** @@ -59,20 +57,11 @@ public function getDefaultArgs(): array public function getDoc(): array { return [ - 'shortdesc' => 'Create project config service class.', - 'synopsis' => [ - [ - 'type' => 'assoc', - 'name' => 'routes_version', - 'description' => 'Define project REST version.', - 'optional' => true, - 'default' => $this->getDefaultArg('routes_version'), - ], - ], + 'shortdesc' => 'Create plugin config service class.', 'longdesc' => $this->prepareLongDesc(" ## USAGE - Used to create project config class with settings like project name, version, REST-API name/version, etc. + Used to create plugin config class with settings like project name, version, REST-API name/version, etc. ## EXAMPLES @@ -82,7 +71,7 @@ public function getDoc(): array ## RESOURCES Service class will be created from this example: - https://github.com/infinum/eightshift-libs/blob/develop/src/Config/ConfigExample.php + https://github.com/infinum/eightshift-libs/blob/develop/src/Config/ConfigPluginExample.php "), ]; } @@ -92,22 +81,16 @@ public function __invoke(array $args, array $assocArgs) { $this->getIntroText($assocArgs); - // Get Props. - $routesVersion = $this->getArg($assocArgs, 'routes_version'); - $className = $this->getClassShortName(); + $newName = 'Config'; // Read the template contents, and replace the placeholders with provided variables. $class = $this->getExampleTemplate(__DIR__, $className) - ->renameClassName($className) + ->renameClassNameWithPrefix($className, $newName) ->renameNamespace($assocArgs) ->renameUse($assocArgs); - if (!empty($routesVersion)) { - $class->searchReplaceString($this->getArgTemplate('routes_version'), $routesVersion); - } - // Output final class to new file/folder and finish. - $class->outputWrite(Components::getProjectPaths('srcDestination', 'Config'), "{$className}.php", $assocArgs); + $class->outputWrite(Components::getProjectPaths('srcDestination', 'Config'), "{$newName}.php", $assocArgs); } } diff --git a/src/Config/ConfigPluginExample.php b/src/Config/ConfigPluginExample.php new file mode 100644 index 000000000..60fbfe208 --- /dev/null +++ b/src/Config/ConfigPluginExample.php @@ -0,0 +1,76 @@ + + */ + public function getDefaultArgs(): array + { + return []; + } + + /** + * Get WPCLI command doc + * + * @return array>|string> + */ + public function getDoc(): array + { + return [ + 'shortdesc' => 'Create theme config service class.', + 'longdesc' => $this->prepareLongDesc(" + ## USAGE + + Used to create theme config class with settings like project name, version, REST-API name/version, etc. + + ## EXAMPLES + + # Create service class: + $ wp {$this->commandParentName} {$this->getCommandParentName()} {$this->getCommandName()} + + ## RESOURCES + + Service class will be created from this example: + https://github.com/infinum/eightshift-libs/blob/develop/src/Config/ConfigThemeExample.php + "), + ]; + } + + /* @phpstan-ignore-next-line */ + public function __invoke(array $args, array $assocArgs) + { + $this->getIntroText($assocArgs); + + $className = $this->getClassShortName(); + $newName = 'Config'; + + // Read the template contents, and replace the placeholders with provided variables. + $class = $this->getExampleTemplate(__DIR__, $className) + ->renameClassNameWithPrefix($className, $newName) + ->renameNamespace($assocArgs) + ->renameUse($assocArgs); + + // Output final class to new file/folder and finish. + $class->outputWrite(Components::getProjectPaths('srcDestination', 'Config'), "{$newName}.php", $assocArgs); + } +} diff --git a/src/Config/ConfigExample.php b/src/Config/ConfigThemeExample.php similarity index 75% rename from src/Config/ConfigExample.php rename to src/Config/ConfigThemeExample.php index c4bb760de..2ac9a8611 100644 --- a/src/Config/ConfigExample.php +++ b/src/Config/ConfigThemeExample.php @@ -13,12 +13,12 @@ namespace EightshiftBoilerplate\Config; -use EightshiftLibs\Config\AbstractConfigData; +use EightshiftLibs\Helpers\Helpers; /** * The project config class. */ -class ConfigExample extends AbstractConfigData +class ConfigThemeExample { /** * Method that returns project name. @@ -27,7 +27,7 @@ class ConfigExample extends AbstractConfigData */ public static function getProjectName(): string { - return \wp_get_theme('', \dirname(__DIR__, 3))->get('TextDomain'); + return Helpers::getThemeName(); } /** @@ -37,7 +37,17 @@ public static function getProjectName(): string */ public static function getProjectVersion(): string { - return \wp_get_theme('', \dirname(__DIR__, 3))->get('Version'); + return Helpers::getThemeVersion(); + } + + /** + * Method that returns project text domain. + * + * Generally used for caching and translations. + */ + public static function getProjectTextDomain(): string + { + return Helpers::getThemeTextDomain(); } /** @@ -49,7 +59,7 @@ public static function getProjectVersion(): string */ public static function getProjectRoutesNamespace(): string { - return static::getProjectName(); + return self::getProjectName(); } /** @@ -61,6 +71,6 @@ public static function getProjectRoutesNamespace(): string */ public static function getProjectRoutesVersion(): string { - return 'v%routes_version%'; + return 'v1'; } } diff --git a/src/CustomPostType/AbstractPostType.php b/src/CustomPostType/AbstractPostType.php index ba5e11cae..29528cd83 100644 --- a/src/CustomPostType/AbstractPostType.php +++ b/src/CustomPostType/AbstractPostType.php @@ -11,18 +11,12 @@ namespace EightshiftLibs\CustomPostType; use EightshiftLibs\Services\ServiceInterface; -use EightshiftLibs\Helpers\LabelGeneratorTrait; /** * Abstract class AbstractPostType class. */ abstract class AbstractPostType implements ServiceInterface { - /** - * Label Generator Helper. - */ - use LabelGeneratorTrait; - /** * Register custom post type. * diff --git a/src/CustomPostType/PostTypeCli.php b/src/CustomPostType/PostTypeCli.php index 0e0a2206a..42ff68d45 100644 --- a/src/CustomPostType/PostTypeCli.php +++ b/src/CustomPostType/PostTypeCli.php @@ -168,9 +168,7 @@ public function __invoke(array $args, array $assocArgs) ->searchReplaceString($this->getArgTemplate('rewrite_url'), $rewriteUrl) ->searchReplaceString($this->getArgTemplate('rest_endpoint_slug'), $restEndpointSlug) ->searchReplaceString($this->getArgTemplate('label'), $label) - ->searchReplaceString($this->getArgTemplate('label_lowercaps'), \strtolower($label)) - ->searchReplaceString($this->getArgTemplate('plural_label'), $pluralLabel) - ->searchReplaceString($this->getArgTemplate('plural_label_lowecaps'), \strtolower($pluralLabel)); + ->searchReplaceString($this->getArgTemplate('plural_label'), $pluralLabel); if (!empty($capability)) { $class->searchReplaceString($this->getArgTemplate('capability'), $capability); diff --git a/src/CustomPostType/PostTypeExample.php b/src/CustomPostType/PostTypeExample.php index 8fb5e23b1..6b8243b75 100644 --- a/src/CustomPostType/PostTypeExample.php +++ b/src/CustomPostType/PostTypeExample.php @@ -76,34 +76,21 @@ protected function getPostTypeSlug(): string */ protected function getPostTypeArguments(): array { - $nouns = [ - \esc_html_x( - '%label%', - 'post type upper case singular name', - 'eightshift-libs' - ), - \esc_html_x( - '%label_lowercaps%', - 'post type lower case singular name', - 'eightshift-libs' - ), - \esc_html_x( - '%plural_label%', - 'post type upper case plural name', - 'eightshift-libs' - ), - \esc_html_x( - '%plural_label_lowecaps%', - 'post type lower case plural name', - 'eightshift-libs' - ), - ]; - - $labels = $this->getGeneratedLabels($nouns); - return [ - 'label' => $nouns[0], - 'labels' => $labels, + // phpcs:disable SlevomatCodingStandard.Namespaces.FullyQualifiedGlobalFunctions.NonFullyQualified + 'labels' => [ + 'name' => esc_html_x( + '%label%', + 'post type plural name', + 'eightshift-libs' + ), + 'singular_name' => esc_html_x( + '%plural_label%', + 'post type singular name', + 'eightshift-libs' + ), + ], + // phpcs:enable 'public' => true, 'publicly_queryable' => true, 'show_ui' => true, diff --git a/src/CustomTaxonomy/AbstractTaxonomy.php b/src/CustomTaxonomy/AbstractTaxonomy.php index 1d6aecd20..2c3a1cfab 100644 --- a/src/CustomTaxonomy/AbstractTaxonomy.php +++ b/src/CustomTaxonomy/AbstractTaxonomy.php @@ -11,18 +11,12 @@ namespace EightshiftLibs\CustomTaxonomy; use EightshiftLibs\Services\ServiceInterface; -use EightshiftLibs\Helpers\LabelGeneratorTrait; /** * Abstract class AbstractBaseTaxonomy class. */ abstract class AbstractTaxonomy implements ServiceInterface { - /** - * Label Generator Helper. - */ - use LabelGeneratorTrait; - /** * Register custom taxonomy. * diff --git a/src/CustomTaxonomy/TaxonomyCli.php b/src/CustomTaxonomy/TaxonomyCli.php index 0fa378698..c4224bfb6 100644 --- a/src/CustomTaxonomy/TaxonomyCli.php +++ b/src/CustomTaxonomy/TaxonomyCli.php @@ -140,9 +140,7 @@ public function __invoke(array $args, array $assocArgs) ->searchReplaceString($this->getArgTemplate('rest_endpoint_slug'), $restEndpointSlug) ->searchReplaceString($this->getArgTemplate('post_type_slug'), $postTypeSlug) ->searchReplaceString($this->getArgTemplate('label'), $label) - ->searchReplaceString($this->getArgTemplate('label_lowercaps'), \strtolower($label)) ->searchReplaceString($this->getArgTemplate('plural_label'), $pluralLabel) - ->searchReplaceString($this->getArgTemplate('plural_label_lowecaps'), \strtolower($pluralLabel)) ->outputWrite(Components::getProjectPaths('srcDestination', 'CustomTaxonomy'), "{$className}.php", $assocArgs); } } diff --git a/src/CustomTaxonomy/TaxonomyExample.php b/src/CustomTaxonomy/TaxonomyExample.php index eb26c6d66..754b4edd6 100644 --- a/src/CustomTaxonomy/TaxonomyExample.php +++ b/src/CustomTaxonomy/TaxonomyExample.php @@ -65,35 +65,22 @@ protected function getPostTypeSlug() */ protected function getTaxonomyArguments(): array { - $nouns = [ - \esc_html_x( - '%label%', - 'taxonomy upper case singular name', - 'eightshift-libs' - ), - \esc_html_x( - '%label_lowercaps%', - 'taxonomy lower case singular name', - 'eightshift-libs' - ), - \esc_html_x( - '%plural_label%', - 'taxonomy upper case plural name', - 'eightshift-libs' - ), - \esc_html_x( - '%plural_label_lowecaps%', - 'taxonomy lower case plural name', - 'eightshift-libs' - ), - ]; - - $labels = $this->getGeneratedLabels($nouns); - return [ + // phpcs:disable SlevomatCodingStandard.Namespaces.FullyQualifiedGlobalFunctions.NonFullyQualified + 'labels' => [ + 'name' => esc_html_x( + '%label%', + 'taxonomy plural name', + 'eightshift-libs' + ), + 'singular_name' => esc_html_x( + '%plural_label%', + 'taxonomy singular name', + 'eightshift-libs' + ), + ], + // phpcs:enable 'hierarchical' => true, - 'label' => $nouns[0], - 'labels' => $labels, 'show_ui' => true, 'show_admin_column' => true, 'show_in_nav_menus' => false, diff --git a/src/Enqueue/AbstractAssets.php b/src/Enqueue/AbstractAssets.php index f7ac6058a..1eea25104 100644 --- a/src/Enqueue/AbstractAssets.php +++ b/src/Enqueue/AbstractAssets.php @@ -10,6 +10,9 @@ namespace EightshiftLibs\Enqueue; +use EightshiftLibs\Cache\AbstractManifestCache; +use EightshiftLibs\Cache\ManifestCacheInterface; +use EightshiftLibs\Exception\InvalidManifest; use EightshiftLibs\Services\ServiceInterface; /** @@ -22,6 +25,23 @@ */ abstract class AbstractAssets implements ServiceInterface { + /** + * Instance variable for manifest cache. + * + * @var ManifestCacheInterface + */ + protected $manifestCache; + + /** + * Create a new instance. + * + * @param ManifestCacheInterface $manifestCache Inject manifest cache. + */ + public function __construct(ManifestCacheInterface $manifestCache) + { + $this->manifestCache = $manifestCache; + } + /** * Media style const */ @@ -129,6 +149,54 @@ protected function scriptInFooter(): bool return static::IN_FOOTER; } + /** + * Load script 'defer' or 'async'. + * + * @link https://developer.wordpress.org/reference/functions/wp_enqueue_style/ + * + * @return string Whether to enqueue the script normally, with defer or async. + * Default value: normal + */ + protected function scriptStrategy(): string + { + return ''; + } + + /** + * Additional script args. + * + * @link https://developer.wordpress.org/reference/functions/wp_enqueue_style/ + * + * @return array Additional script args. + */ + protected function scriptArgs(): array + { + return [ + 'strategy' => $this->scriptStrategy(), + 'in_footer' => $this->scriptInFooter(), + ]; + } + + /** + * Get the manifest data. + * + * @param string $key The key from the manifest.json file. + * + * @throws InvalidManifest If manifest key is missing. + * + * @return string The value from the manifest.json file. + */ + public function setAssetsItem(string $key): string + { + $data = $this->manifestCache->getManifestCacheTopItem(AbstractManifestCache::ASSETS_KEY, AbstractManifestCache::TYPE_ASSETS); + + if (!isset($data[$key])) { + throw InvalidManifest::missingManifestKeyException($key, 'public'); + } + + return $data[$key] ?? ''; + } + /** * Method that returns assets name used to prefix asset handlers. * diff --git a/src/Enqueue/Admin/AbstractEnqueueAdmin.php b/src/Enqueue/Admin/AbstractEnqueueAdmin.php index 308ebb3ec..f69537e4b 100644 --- a/src/Enqueue/Admin/AbstractEnqueueAdmin.php +++ b/src/Enqueue/Admin/AbstractEnqueueAdmin.php @@ -11,7 +11,6 @@ namespace EightshiftLibs\Enqueue\Admin; use EightshiftLibs\Enqueue\AbstractAssets; -use EightshiftLibs\Manifest\ManifestInterface; /** * Class EnqueueAdmin @@ -20,15 +19,19 @@ */ abstract class AbstractEnqueueAdmin extends AbstractAssets { + /** + * Admin script handle. + * + * @return string + */ public const ADMIN_SCRIPT_URI = 'applicationAdmin.js'; - public const ADMIN_STYLE_URI = 'applicationAdmin.css'; /** - * Instance variable of manifest data. + * Admin style handle. * - * @var ManifestInterface + * @return string */ - protected ManifestInterface $manifest; + public const ADMIN_STYLE_URI = 'applicationAdmin.css'; /** * Get admin Stylesheet handle. @@ -70,7 +73,7 @@ public function enqueueStyles(string $hook): void \wp_register_style( $handle, - $this->manifest->getAssetsManifestItem(static::ADMIN_STYLE_URI), + $this->setAssetsItem(static::ADMIN_STYLE_URI), $this->getAdminStyleDependencies(), $this->getAssetsVersion(), $this->getMedia() @@ -120,10 +123,10 @@ public function enqueueScripts(string $hook): void \wp_register_script( $handle, - $this->manifest->getAssetsManifestItem(static::ADMIN_SCRIPT_URI), + $this->setAssetsItem(static::ADMIN_SCRIPT_URI), $this->getAdminScriptDependencies(), $this->getAssetsVersion(), - $this->scriptInFooter() + \is_wp_version_compatible('6.3') ? $this->scriptArgs() : $this->scriptInFooter() ); \wp_enqueue_script($handle); diff --git a/src/Enqueue/Admin/EnqueueAdminExample.php b/src/Enqueue/Admin/EnqueueAdminExample.php index e5afba4ff..d1c12ab63 100644 --- a/src/Enqueue/Admin/EnqueueAdminExample.php +++ b/src/Enqueue/Admin/EnqueueAdminExample.php @@ -11,7 +11,6 @@ namespace EightshiftBoilerplate\Enqueue\Admin; use EightshiftBoilerplate\Config\Config; -use EightshiftLibs\Manifest\ManifestInterface; use EightshiftLibs\Enqueue\Admin\AbstractEnqueueAdmin; /** @@ -21,16 +20,6 @@ */ class EnqueueAdminExample extends AbstractEnqueueAdmin { - /** - * Create a new admin instance. - * - * @param ManifestInterface $manifest Inject manifest which holds data about assets from manifest.json. - */ - public function __construct(ManifestInterface $manifest) - { - $this->manifest = $manifest; - } - /** * Register all the hooks * diff --git a/src/Enqueue/Blocks/AbstractEnqueueBlocks.php b/src/Enqueue/Blocks/AbstractEnqueueBlocks.php index 2f0ba41b0..c295b1a2c 100644 --- a/src/Enqueue/Blocks/AbstractEnqueueBlocks.php +++ b/src/Enqueue/Blocks/AbstractEnqueueBlocks.php @@ -11,27 +11,46 @@ namespace EightshiftLibs\Enqueue\Blocks; use EightshiftLibs\Enqueue\AbstractAssets; -use EightshiftLibs\Manifest\ManifestInterface; /** * Enqueue_Blocks class. */ abstract class AbstractEnqueueBlocks extends AbstractAssets { + /** + * Block editor script handle. + * + * @return string + */ public const BLOCKS_EDITOR_SCRIPT_URI = 'applicationBlocksEditor.js'; + + /** + * Block editor style handle. + * + * @return string + */ public const BLOCKS_EDITOR_STYLE_URI = 'applicationBlocksEditor.css'; + /** + * Block style handle. + * + * @return string + */ public const BLOCKS_STYLE_URI = 'applicationBlocks.css'; + /** + * Block frontend style handle. + * + * @return string + */ public const BLOCKS_FRONTEND_STYLE_URI = 'applicationBlocksFrontend.css'; - public const BLOCKS_FRONTEND_SCRIPT_URI = 'applicationBlocksFrontend.js'; /** - * Instance variable of manifest data. + * Block frontend script handle. * - * @var ManifestInterface + * @return string */ - protected ManifestInterface $manifest; + public const BLOCKS_FRONTEND_SCRIPT_URI = 'applicationBlocksFrontend.js'; /** * Get block editor JavaScript handle. @@ -72,10 +91,10 @@ public function enqueueBlockEditorScript(string $hook): void \wp_register_script( $handle, - $this->manifest->getAssetsManifestItem(static::BLOCKS_EDITOR_SCRIPT_URI), + $this->setAssetsItem(static::BLOCKS_EDITOR_SCRIPT_URI), $this->getAdminScriptDependencies(), $this->getAssetsVersion(), - $this->scriptInFooter() + \is_wp_version_compatible('6.3') ? $this->scriptArgs() : $this->scriptInFooter() ); \wp_enqueue_script($handle); @@ -124,7 +143,7 @@ public function enqueueBlockEditorStyle(string $hook): void \wp_register_style( $handle, - $this->manifest->getAssetsManifestItem(static::BLOCKS_EDITOR_STYLE_URI), + $this->setAssetsItem(static::BLOCKS_EDITOR_STYLE_URI), $this->getAdminStyleDependencies(), $this->getAssetsVersion(), $this->getMedia() @@ -172,7 +191,7 @@ public function enqueueBlockStyle(string $hook): void \wp_register_style( $handle, - $this->manifest->getAssetsManifestItem(static::BLOCKS_STYLE_URI), + $this->setAssetsItem(static::BLOCKS_STYLE_URI), $this->getFrontendStyleDependencies(), $this->getAssetsVersion(), $this->getMedia() @@ -186,7 +205,7 @@ public function enqueueBlockStyle(string $hook): void * * @return string */ - public function getBlockFrontentScriptHandle(): string + public function getBlockFrontendScriptHandle(): string { return "{$this->getAssetsPrefix()}-block-frontend-scripts"; } @@ -216,21 +235,21 @@ public function enqueueBlockFrontendScript(string $hook): void return; } - $handle = $this->getBlockFrontentScriptHandle(); + $handle = $this->getBlockFrontendScriptHandle(); \wp_register_script( $handle, - $this->manifest->getAssetsManifestItem(static::BLOCKS_FRONTEND_SCRIPT_URI), + $this->setAssetsItem(static::BLOCKS_FRONTEND_SCRIPT_URI), $this->getFrontendScriptDependencies(), $this->getAssetsVersion(), - $this->scriptInFooter() + \is_wp_version_compatible('6.3') ? $this->scriptArgs() : $this->scriptInFooter() ); \wp_enqueue_script($handle); foreach ($this->getLocalizations() as $objectName => $dataArray) { - \wp_localize_script($this->getBlockFrontentScriptHandle(), $objectName, $dataArray); + \wp_localize_script($this->getBlockFrontendScriptHandle(), $objectName, $dataArray); } } @@ -239,7 +258,7 @@ public function enqueueBlockFrontendScript(string $hook): void * * @return string */ - public function getBlockFrontentStyleHandle(): string + public function getBlockFrontendStyleHandle(): string { return "{$this->getAssetsPrefix()}-block-frontend-style"; } @@ -269,11 +288,11 @@ public function enqueueBlockFrontendStyle(string $hook): void return; } - $handle = $this->getBlockFrontentStyleHandle(); + $handle = $this->getBlockFrontendStyleHandle(); \wp_register_style( $handle, - $this->manifest->getAssetsManifestItem(static::BLOCKS_FRONTEND_STYLE_URI), + $this->setAssetsItem(static::BLOCKS_FRONTEND_STYLE_URI), $this->getFrontendStyleDependencies(), $this->getAssetsVersion(), $this->getMedia() diff --git a/src/Enqueue/Blocks/EnqueueBlocksExample.php b/src/Enqueue/Blocks/EnqueueBlocksExample.php index 22111b082..59f36fffc 100644 --- a/src/Enqueue/Blocks/EnqueueBlocksExample.php +++ b/src/Enqueue/Blocks/EnqueueBlocksExample.php @@ -11,7 +11,6 @@ namespace EightshiftBoilerplate\Enqueue\Blocks; use EightshiftBoilerplate\Config\Config; -use EightshiftLibs\Manifest\ManifestInterface; use EightshiftLibs\Enqueue\Blocks\AbstractEnqueueBlocks; /** @@ -19,16 +18,6 @@ */ class EnqueueBlocksExample extends AbstractEnqueueBlocks { - /** - * Create a new admin instance. - * - * @param ManifestInterface $manifest Inject manifest which holds data about assets from manifest.json. - */ - public function __construct(ManifestInterface $manifest) - { - $this->manifest = $manifest; - } - /** * Register all the hooks */ diff --git a/src/Enqueue/Theme/AbstractEnqueueTheme.php b/src/Enqueue/Theme/AbstractEnqueueTheme.php index 6b9d22b2d..3139e85b5 100644 --- a/src/Enqueue/Theme/AbstractEnqueueTheme.php +++ b/src/Enqueue/Theme/AbstractEnqueueTheme.php @@ -11,22 +11,25 @@ namespace EightshiftLibs\Enqueue\Theme; use EightshiftLibs\Enqueue\AbstractAssets; -use EightshiftLibs\Manifest\ManifestInterface; /** * Class Enqueue */ abstract class AbstractEnqueueTheme extends AbstractAssets { + /** + * Get the theme script handle. + * + * @return array + */ public const THEME_SCRIPT_URI = 'application.js'; - public const THEME_STYLE_URI = 'application.css'; /** - * Instance variable of manifest data. + * Get the theme style handle. * - * @var ManifestInterface + * @return array */ - protected ManifestInterface $manifest; + public const THEME_STYLE_URI = 'application.css'; /** * Get theme Stylesheet handle. @@ -77,7 +80,7 @@ public function enqueueStyles(string $hook): void \wp_register_style( $handle, - $this->manifest->getAssetsManifestItem(static::THEME_STYLE_URI), + $this->setAssetsItem(static::THEME_STYLE_URI), $this->getFrontendStyleDependencies(), $this->getAssetsVersion(), $this->getMedia() @@ -97,10 +100,10 @@ public function enqueueScripts(): void \wp_register_script( $handle, - $this->manifest->getAssetsManifestItem(static::THEME_SCRIPT_URI), + $this->setAssetsItem(static::THEME_SCRIPT_URI), $this->getFrontendScriptDependencies(), $this->getAssetsVersion(), - $this->scriptInFooter() + \is_wp_version_compatible('6.3') ? $this->scriptArgs() : $this->scriptInFooter() ); \wp_enqueue_script($handle); diff --git a/src/Enqueue/Theme/EnqueueThemeExample.php b/src/Enqueue/Theme/EnqueueThemeExample.php index 4f6b042c1..69ae82e47 100644 --- a/src/Enqueue/Theme/EnqueueThemeExample.php +++ b/src/Enqueue/Theme/EnqueueThemeExample.php @@ -11,7 +11,6 @@ namespace EightshiftBoilerplate\Enqueue\Theme; use EightshiftBoilerplate\Config\Config; -use EightshiftLibs\Manifest\ManifestInterface; use EightshiftLibs\Enqueue\Theme\AbstractEnqueueTheme; /** @@ -19,16 +18,6 @@ */ class EnqueueThemeExample extends AbstractEnqueueTheme { - /** - * Create a new admin instance. - * - * @param ManifestInterface $manifest Inject manifest which holds data about assets from manifest.json. - */ - public function __construct(ManifestInterface $manifest) - { - $this->manifest = $manifest; - } - /** * Register all the hooks * diff --git a/src/Exception/ComponentException.php b/src/Exception/ComponentException.php index 6da0ca01c..357e6c978 100644 --- a/src/Exception/ComponentException.php +++ b/src/Exception/ComponentException.php @@ -41,38 +41,4 @@ public static function throwNotStringOrArray($variable): ComponentException return new ComponentException($output); } - - /** - * Throws exception if unable to locate component. - * - * @param string $component Missing component name. - * @return static - */ - public static function throwUnableToLocateComponent(string $component): ComponentException - { - return new ComponentException( - \sprintf( - /* translators: %s is replaced with the path of the component. */ - \esc_html__('Unable to locate component by path: %s', 'eightshift-libs'), - $component - ) - ); - } - - /** - * Throws exception if unable to locate partial. - * - * @param string $path Path. - * @return static - */ - public static function throwUnableToLocatePartial(string $path): ComponentException - { - return new ComponentException( - \sprintf( - /* translators: %s is replaced with the path of the partial. */ - \esc_html__('Unable to locate partial on this path: %s', 'eightshift-libs'), - $path - ) - ); - } } diff --git a/src/Exception/FailedToLoadView.php b/src/Exception/FailedToLoadView.php deleted file mode 100644 index a7541c4be..000000000 --- a/src/Exception/FailedToLoadView.php +++ /dev/null @@ -1,42 +0,0 @@ -getMessage() - ); - - return new FailedToLoadView($message, $exception->getCode(), $exception); - } -} diff --git a/src/Exception/FileMissing.php b/src/Exception/FileMissing.php deleted file mode 100644 index a6cf8f931..000000000 --- a/src/Exception/FileMissing.php +++ /dev/null @@ -1,38 +0,0 @@ - + * @var ManifestCacheInterface */ - private $countries = []; + protected $manifestCache; + + /** + * Create a new instance. + * + * @param ManifestCacheInterface $manifestCache Inject manifest cache. + */ + public function __construct(ManifestCacheInterface $manifestCache) + { + $this->manifestCache = $manifestCache; + } /** * Get geolocation cookie name. @@ -168,16 +179,17 @@ public function getCountries(): array ], ]; - // Save to internal cache so we don't read manifest all the time. - if (!$this->countries) { - $this->countries = Components::getManifestDirect(__DIR__); - } + $data = $this->manifestCache->getManifestCacheTopItem(AbstractManifestCache::ASSETS_KEY, AbstractManifestCache::TYPE_ASSETS); - foreach ($this->countries as $country) { - $code = $country['Code']; + foreach ($data as $country) { + $code = $country['Code'] ?? ''; + + if (!$code) { + continue; + } $output[] = [ - 'label' => $country['Name'], + 'label' => $country['Name'] ?? '', 'value' => $code, 'group' => [ \strtoupper($code), diff --git a/src/Helpers/ApiTrait.php b/src/Helpers/ApiTrait.php new file mode 100644 index 000000000..6460350b8 --- /dev/null +++ b/src/Helpers/ApiTrait.php @@ -0,0 +1,109 @@ + $additional Additonal data to attach to response. + * + * @return array|int|string> + */ + public static function getApiSuccessPublicOutput(string $msg, array $additional = []): array + { + $output = [ + 'status' => AbstractRoute::STATUS_SUCCESS, + 'code' => AbstractRoute::API_RESPONSE_CODE_SUCCESS, + 'message' => $msg, + ]; + + if ($additional) { + $output['data'] = $additional; + } + + return $output; + } + + /** + * Return API warning response array. + * + * @param string $msg Msg for the user. + * @param array $additional Additonal data to attach to response. + * + * @return array|int|string> + */ + public static function getApiWarningPublicOutput(string $msg, array $additional = []): array + { + $output = [ + 'status' => AbstractRoute::STATUS_WARNING, + 'code' => AbstractRoute::API_RESPONSE_CODE_SUCCESS, + 'message' => $msg, + ]; + + if ($additional) { + $output['data'] = $additional; + } + + return $output; + } + + /** + * Return API error response array. + * + * @param string $msg Message for the user. + * @param array $additional Additonal data to attach to response. + * + * @return array|int|string> + */ + public static function getApiErrorPublicOutput(string $msg, array $additional = []): array + { + $output = [ + 'status' => AbstractRoute::STATUS_ERROR, + 'code' => AbstractRoute::API_RESPONSE_CODE_ERROR, + 'message' => $msg, + ]; + + if ($additional) { + $output['data'] = $additional; + } + + return $output; + } + + /** + * Get API route URL data. + * + * @param string $namespace The namespace. + * @param string $version The version. + * @param string $path The path. + * + * @return array + */ + public static function getApiRouteUrlData(string $namespace, string $version, string $path): array + { + $prefix = \rtrim(\get_rest_url(\get_current_blog_id()), '/'); + + return [ + 'prefix' => $prefix, + 'namespace' => $namespace, + 'version' => $version, + 'url' => "{$prefix}/{$namespace}/{$version}/{$path}", + ]; + } +} diff --git a/src/Helpers/AttributesTrait.php b/src/Helpers/AttributesTrait.php index 820717c59..b9e430274 100644 --- a/src/Helpers/AttributesTrait.php +++ b/src/Helpers/AttributesTrait.php @@ -144,7 +144,7 @@ public static function getAttrKey(string $key, array $attributes, array $manifes // No need to test if this is block or component because on top level block there is no prefix. // If there is a prefix, remove the attribute component name prefix and replace it with the new prefix. - return (string)\str_replace(Components::kebabToCamelCase($manifest['componentName']), $attributes['prefix'], $key); + return (string)\str_replace(Helpers::kebabToCamelCase($manifest['componentName']), $attributes['prefix'], $key); } /** @@ -181,13 +181,13 @@ public static function props(string $newName, array $attributes, array $manual = $blockName = $attributes['blockName'] ?? ''; // Populate prefix key for recursive checks of attribute names. - $prefix = (!isset($attributes['prefix'])) ? Components::kebabToCamelCase($blockName) : $attributes['prefix']; + $prefix = (!isset($attributes['prefix'])) ? Helpers::kebabToCamelCase($blockName) : $attributes['prefix']; // Set component prefix. if (empty($prefix)) { - $output['prefix'] = Components::kebabToCamelCase($newName); + $output['prefix'] = Helpers::kebabToCamelCase($newName); } else { - $output['prefix'] = $prefix . \ucfirst(Components::kebabToCamelCase($newName)); + $output['prefix'] = $prefix . \ucfirst(Helpers::kebabToCamelCase($newName)); } // Iterate over the attributes. @@ -215,7 +215,7 @@ public static function props(string $newName, array $attributes, array $manual = } // Remove the current component name from the attribute name. - $newKey = \str_replace(\lcfirst(Components::kebabToCamelCase($newName)), '', $key); + $newKey = \str_replace(\lcfirst(Helpers::kebabToCamelCase($newName)), '', $key); // Remove the old key. unset($manual[$key]); diff --git a/src/Helpers/Components.php b/src/Helpers/Components.php index 1e6e56769..2c90b3bfb 100644 --- a/src/Helpers/Components.php +++ b/src/Helpers/Components.php @@ -10,516 +10,11 @@ namespace EightshiftLibs\Helpers; -use EightshiftLibs\Exception\ComponentException; - /** * Helpers for components + * + * @deprecated 8.0.0 Please use Helpers class from EightshiftLibs\Helpers. This class will be removed in the next major release. */ -class Components +class Components extends Helpers { - /** - * Store trait. - */ - use StoreTrait; - - /** - * Css Variables trait. - */ - use CssVariablesTrait; - - /** - * Selectors trait. - */ - use SelectorsTrait; - - /** - * Attributes trait. - */ - use AttributesTrait; - - /** - * Generic object helper trait. - */ - use ObjectHelperTrait; - - /** - * Shortcode trait. - */ - use ShortcodeTrait; - - /** - * Post trait. - */ - use PostTrait; - - /** - * Label Generator trait. - */ - use LabelGeneratorTrait; - - /** - * Media trait. - */ - use MediaTrait; - - /** - * Get all project paths for store. - * - * @var array - */ - public const PROJECT_PATHS = [ - 'projectRoot', - 'srcDestination', - 'cliOutput', - 'wpContent', - 'libs', - - 'blocksGlobalAssetsSource', - 'blocksAssetsSource', - 'blocksSource', - 'blocksSourceCustom', - 'blocksSourceComponents', - 'blocksSourceVariations', - 'blocksSourceWrapper', - - 'blocksGlobalAssetsDestination', - 'blocksAssetsDestination', - 'blocksDestination', - 'blocksDestinationCustom', - 'blocksDestinationComponents', - 'blocksDestinationVariations', - 'blocksDestinationWrapper', - ]; - - /** - * Renders a components and (optionally) passes some attributes to it. - * - * Note about "parentClass" attribute: If provided, the component will be wrapped with a - * parent BEM selector. For example, if $attributes['parentClass'] === 'header' and $component === 'logo' - * are set, the component will be wrapped with a . - * - * @param string $component Component's name or full path (ending with .php). - * @param array $attributes Array of attributes that's implicitly passed to component. - * @param string $parentPath If parent path is provides it will be appended to the file location. - * If not get_template_directory_uri() will be used as a default parent path. - * @param bool $useComponentDefaults If true the helper will fetch component manifest and merge default attributes in the original attributes list. - * - * @throws ComponentException When we're unable to find the component by $component. - * - * @return string - */ - public static function render(string $component, array $attributes = [], string $parentPath = '', bool $useComponentDefaults = false): string - { - $sep = \DIRECTORY_SEPARATOR; - - // If parent path is missing provide project root. - if (!$parentPath) { - $parentPath = Components::getProjectPaths('root'); - } else { - // Remove slash. - $parentPath = \trim($parentPath, $sep); - $parentPath = "{$sep}{$parentPath}{$sep}"; - } - - /** - * Detect if user passed component name or path. - * - * If the path was passed, we need to get the component name, in case the - * parentClass attribute was added, because the class of the wrapper need to look like - * - * parentClass__componentName - * - * not - * - * parentClass__componentName.php - */ - if (\strpos($component, '.php') !== false) { - $component = \ltrim($component, $sep); - $componentPath = $component; - } else { - if ($component === 'wrapper') { - $componentPath = Components::getProjectPaths('blocksDestinationWrapper', "{$component}.php", $sep); - } else { - $componentPath = Components::getProjectPaths('blocksDestinationComponents', "{$component}{$sep}{$component}.php", $sep); - } - } - - $componentPath = "{$parentPath}{$componentPath}"; - - if ($useComponentDefaults) { - $manifest = Components::getManifest($componentPath); - } - - if (!\file_exists($componentPath)) { - throw ComponentException::throwUnableToLocateComponent($componentPath); - } - - // Merge default attributes with the component attributes. - if ($useComponentDefaults && isset($manifest['attributes'])) { - $attributes = Components::getDefaultRenderAttributes($manifest, $attributes); - } - - \ob_start(); - - // Wrap component with parent BEM selector if parent's class is provided. Used - // for setting specific styles for components rendered inside other components. - if (isset($attributes['parentClass'])) { - $component = \str_replace('.php', '', $component); - \printf('
', \esc_attr("{$attributes['parentClass']}__{$component}")); // phpcs:ignore Eightshift.Security.CustomEscapeOutput.OutputNotEscaped - } - - require $componentPath; - - if (isset($attributes['parentClass'])) { - echo '
'; // phpcs:ignore Eightshift.Security.CustomEscapeOutput.OutputNotEscaped - } - - return \trim((string) \ob_get_clean()); - } - - /** - * Render component/block partial. - * - * @param string $type Type of content block, component, variable, etc. - * @param string $parent Parent block/component name. - * @param string $name Name of the partial. It can be without extension so .php is used. - * @param array $attributes Attributes that will be passed to partial. - * @param string $partialFolderName Partial folder name. - * - * @throws ComponentException When we're unable to find the partial. - * - * @return string Partial html. - */ - public static function renderPartial( - string $type, - string $parent, - string $name, - array $attributes = [], - string $partialFolderName = 'partials' - ): string { - $sep = \DIRECTORY_SEPARATOR; - - // If no extension is provided use php. - if (\strpos($name, '.php') === false) { - $name = "{$name}.php"; - } - - $partialPath = "{$parent}{$sep}{$partialFolderName}{$sep}{$name}"; - - // Detect folder based on the name. - switch ($type) { - case 'block': - case 'blocks': - case 'custom': - $path = Components::getProjectPaths('blocksDestinationCustom', $partialPath); - break; - case 'component': - case 'components': - $path = Components::getProjectPaths('blocksDestinationComponents', $partialPath); - break; - case 'variation': - case 'variations': - $path = Components::getProjectPaths('blocksDestinationVariations', $partialPath); - break; - case 'wrapper': - $path = Components::getProjectPaths('blocksDestinationWrapper', $partialPath); - break; - default: - $path = Components::getProjectPaths('root', $partialPath); - break; - } - - // Bailout if file is missing. - if (!\file_exists($path)) { - throw ComponentException::throwUnableToLocatePartial($path); - } - - \ob_start(); - - require $path; - - return \trim((string) \ob_get_clean()); - } - - /** - * Get manifest json. Generally used for getting block/components manifest. - * - * @param string $path Absolute path to manifest folder. - * @param string $name Block/Component name. - * - * @throws ComponentException When we're unable to find the component by $component. - * - * @return array - */ - public static function getManifest(string $path, string $name = ''): array - { - $pathNew = \trim($path, \DIRECTORY_SEPARATOR); - - $pathNew = \explode(\DIRECTORY_SEPARATOR, $pathNew); - - // If user provides url with .php at the end. - if (\strpos(\end($pathNew), '.php') !== false) { - \array_pop($pathNew); - } - - // Find last item to get name. - $item = $pathNew[\count($pathNew) - 1] ?? ''; - - // Settings details. - if ($item === 'Blocks' || $path === 'settings') { - return Components::getSettings(); - } - - // Wrapper details. - if ($item === 'wrapper' || $path === 'wrapper') { - return Components::getWrapper(); - } - - $type = $pathNew[\count($pathNew) - 2] ?? ''; - $itemName = $item; - - if ($name) { - $itemName = $name; - } - - // Components settings. - if ($type === 'components' || $path === 'component') { - return Components::getComponent($itemName); - } - - // Blocks settings. - if ($type === 'custom' || $path === 'block') { - return Components::getBlock($itemName); - } - - return []; - } - - /** - * Get manifest json. Generally used for getting block/components manifest. Used to directly fetch json file. - * Used in combination with getManifest helper. - * - * @param string $path Absolute path to manifest folder. - * - * @throws ComponentException When we're unable to find the component by $component. - * - * @return array - */ - public static function getManifestDirect(string $path): array - { - $sep = \DIRECTORY_SEPARATOR; - $path = \rtrim($path, $sep); - - $manifest = "{$path}{$sep}manifest.json"; - - if (!\file_exists($manifest)) { - throw ComponentException::throwUnableToLocateComponent($manifest); - } - - return \json_decode(\implode(' ', (array)\file($manifest)), true); - } - - /** - * Internal helper for getting all project paths for easy mocking in tests. - * - * @param string $type Type fo path to return. - * @param string $suffix Additional suffix path to add. - * @param string $prefix Additional prefix instead of dirname path. - * - * @return string - */ - public static function getProjectPaths(string $type = '', string $suffix = '', string $prefix = ''): string - { - $sep = \DIRECTORY_SEPARATOR; - - $path = ''; - $internalPrefix = \dirname(__FILE__, 6); - - if (\getenv('ES_TEST')) { - $internalPrefix = \dirname(__FILE__, 3); - } - - $flibsPath = ["node_modules", "@eightshift", "frontend-libs", "blocks", "init"]; - $fPLibsPath = ["node_modules", "@eightshift", "frontend-libs-private", "blocks", "init"]; - $libsPath = ["vendor", "infinum", "eightshift-libs"]; - $testsDataPath = ["tests", "data"]; - $srcPath = "src"; - $blocksPath = [$srcPath, "Blocks"]; - $assetsPath = "assets"; - $cliOutputPath = "cliOutput"; - - $name = ''; - - switch ($type) { - case 'projectRoot': - $internalPrefix = \dirname(__FILE__, 9); - - if (\getenv('ES_TEST')) { - $internalPrefix = \dirname(__FILE__, 3); - } - break; - case 'testsData': - if (\getenv('ES_TEST')) { - $internalPrefix = \dirname(__FILE__, 3); - $path = self::joinPaths([...$testsDataPath]); - } - - break; - case 'srcDestination': - $path = $srcPath; - - if (\getenv('ES_TEST')) { - $internalPrefix = \dirname(__FILE__, 3); - $path = self::joinPaths([$cliOutputPath, $srcPath]); - } - - break; - case 'cliOutput': - if (\getenv('ES_TEST')) { - $internalPrefix = \dirname(__FILE__, 3); - $path = $cliOutputPath; - } - - break; - case 'wpContent': - $internalPrefix = \dirname(__FILE__, 8); - - if (\getenv('ES_TEST')) { - $internalPrefix = \dirname(__FILE__, 3); - } - break; - case 'libs': - $path = self::joinPaths($libsPath); - - if (\getenv('ES_TEST')) { - $path = ''; - } - break; - case 'blocksGlobalAssetsSource': - $path = self::joinPaths([...$flibsPath, $assetsPath]); - - if (\getenv('ES_TEST')) { - $path = self::joinPaths([...$testsDataPath, $assetsPath]); - } - break; - case 'blocksAssetsSource': - $path = self::joinPaths([...$flibsPath, ...$blocksPath, $assetsPath]); - - if (\getenv('ES_TEST')) { - $path = self::joinPaths([...$testsDataPath, ...$blocksPath, $assetsPath]); - } - break; - case 'blocksSource': - $path = self::joinPaths([...$flibsPath, ...$blocksPath]); - - if (\getenv('ES_TEST')) { - $path = self::joinPaths([...$testsDataPath, ...$blocksPath]); - } - break; - case 'blocksPrivateSource': - $path = self::joinPaths([...$fPLibsPath, ...$blocksPath]); - - if (\getenv('ES_TEST')) { - $path = self::joinPaths([...$testsDataPath, ...$blocksPath]); - } - break; - case 'blocksDestinationCustom': - case 'blocksSourceCustom': - case 'blocksPrivateSourceCustom': - $name = 'custom'; - break; - case 'blocksDestinationComponents': - case 'blocksSourceComponents': - case 'blocksPrivateSourceComponents': - $name = 'components'; - break; - case 'blocksDestinationVariations': - case 'blocksSourceVariations': - case 'blocksPrivateSourceVariations': - $name = 'variations'; - break; - case 'blocksDestinationWrapper': - case 'blocksSourceWrapper': - $name = 'wrapper'; - break; - - case 'blocksGlobalAssetsDestination': - $path = self::joinPaths([$assetsPath]); - - if (\getenv('ES_TEST')) { - $path = self::joinPaths([$cliOutputPath, $assetsPath]); - } - break; - case 'blocksAssetsDestination': - $path = self::joinPaths([...$blocksPath, $assetsPath]); - - if (\getenv('ES_TEST')) { - $path = self::joinPaths([$cliOutputPath, ...$blocksPath, $assetsPath]); - } - break; - case 'blocksDestination': - $path = self::joinPaths($blocksPath); - - if (\getenv('ES_TEST')) { - $path = self::joinPaths([$cliOutputPath, ...$blocksPath]); - } - break; - } - - switch ($type) { - case 'blocksSourceCustom': - case 'blocksSourceComponents': - case 'blocksSourceVariations': - case 'blocksSourceWrapper': - $path = self::joinPaths([...$flibsPath, ...$blocksPath, $name]); - - if (\getenv('ES_TEST')) { - $path = self::joinPaths([...$testsDataPath, ...$blocksPath, $name]); - } - break; - - case 'blocksPrivateSourceCustom': - case 'blocksPrivateSourceComponents': - case 'blocksPrivateSourceVariations': - $path = self::joinPaths([...$fPLibsPath, ...$blocksPath, $name]); - - if (\getenv('ES_TEST')) { - $path = self::joinPaths([...$testsDataPath, ...$blocksPath, $name]); - } - break; - - case 'blocksDestinationCustom': - case 'blocksDestinationComponents': - case 'blocksDestinationVariations': - case 'blocksDestinationWrapper': - $path = self::joinPaths([...$blocksPath, $name]); - - if (\getenv('ES_TEST')) { - $path = self::joinPaths([$cliOutputPath, ...$blocksPath, $name]); - } - break; - } - - if (!$prefix) { - $prefix = $internalPrefix; - } - - $path = self::joinPaths([$prefix, $path, $suffix]); - - return \str_replace("{$sep}{$sep}", $sep, $path); - } - /** - * Paths join - * - * @param array $paths Paths to join. - * - * @return string - */ - public static function joinPaths(array $paths): string - { - $sep = \DIRECTORY_SEPARATOR; - $path = \implode($sep, $paths); - - return \str_replace("{$sep}{$sep}", $sep, $path); - } } diff --git a/src/Helpers/CssVariablesTrait.php b/src/Helpers/CssVariablesTrait.php index 1d016bd50..bdb21109d 100644 --- a/src/Helpers/CssVariablesTrait.php +++ b/src/Helpers/CssVariablesTrait.php @@ -18,16 +18,14 @@ trait CssVariablesTrait /** * Get Global Manifest.json and return globalVariables as CSS variables. * - * @param array $globalManifest Global manifest array. - Deprecated. - * * @return string */ - public static function outputCssVariablesGlobal(array $globalManifest = []): string + public static function outputCssVariablesGlobal(): string { $output = ''; - foreach (Components::getSettingsGlobalVariables() as $itemKey => $itemValue) { - $itemKey = Components::camelToKebabCase($itemKey); + foreach (Helpers::getSettingsGlobalVariables() as $itemKey => $itemValue) { + $itemKey = Helpers::camelToKebabCase($itemKey); if (\gettype($itemValue) === 'array') { $output .= self::globalInner($itemValue, $itemKey); @@ -36,11 +34,11 @@ public static function outputCssVariablesGlobal(array $globalManifest = []): str } } - $id = Components::getConfigOutputCssSelectorName(); + $id = Helpers::getConfigOutputCssSelectorName(); $output = ""; - if (Components::getConfigOutputCssOptimize()) { + if (Helpers::getConfigOutputCssOptimize()) { $output = \str_replace(["\n", "\r"], '', $output); } @@ -53,12 +51,11 @@ public static function outputCssVariablesGlobal(array $globalManifest = []): str * @param array $attributes Built attributes. * @param array $manifest Component/block manifest data. * @param string $unique Unique key. - * @param array $globalManifest Global manifest array. * @param string $customSelector Output custom selector to use as a style prefix. * * @return string */ - public static function outputCssVariables(array $attributes, array $manifest, string $unique, array $globalManifest = [], string $customSelector = ''): string + public static function outputCssVariables(array $attributes, array $manifest, string $unique, string $customSelector = ''): string { // Bailout if manifest is missing variables key. if (!isset($manifest['variables']) && !isset($manifest['variablesCustom'])) { @@ -66,7 +63,7 @@ public static function outputCssVariables(array $attributes, array $manifest, st } // Define variables from globalManifest. - $breakpoints = Components::getSettingsGlobalVariablesBreakpoints(); + $breakpoints = Helpers::getSettingsGlobalVariablesBreakpoints(); // Sort breakpoints in ascending order. \asort($breakpoints); @@ -129,12 +126,12 @@ static function ($key) use ($attributes) { $context = isset($_GET['context']) ? \sanitize_text_field(\wp_unslash($_GET['context'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended // If default output just echo. - if (!Components::getConfigOutputCssGlobally() || (\wp_is_json_request() && $context === 'edit')) { + if (!Helpers::getConfigOutputCssGlobally() || (\wp_is_json_request() && $context === 'edit')) { return self::getCssVariablesTypeDefault($name, $data, $manifest, $unique); } // Set inline styles. - Components::setStyle(self::getCssVariablesTypeInline($name, $data, $manifest, $unique)); + Helpers::setStyle(self::getCssVariablesTypeInline($name, $data, $manifest, $unique)); return ''; } @@ -150,14 +147,14 @@ public static function outputCssVariablesInline(): string $context = isset($_GET['context']) ? \sanitize_text_field(\wp_unslash($_GET['context'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended // If default output just echo. - if (!Components::getConfigOutputCssGlobally() || (\wp_is_json_request() && $context === 'edit')) { + if (!Helpers::getConfigOutputCssGlobally() || (\wp_is_json_request() && $context === 'edit')) { return ''; } // Prepare final output. $output = ''; - $styles = Components::getStyles(); + $styles = Helpers::getStyles(); // Bailout if styles are missing. if ($styles) { @@ -254,15 +251,15 @@ static function () { } // Remove newlines is config is set. - if (Components::getConfigOutputCssOptimize()) { + if (Helpers::getConfigOutputCssOptimize()) { $output = \str_replace(["\n", "\r"], '', $output); } // Add additional style from config settings. - $additionalStyles = Components::getConfigOutputCssGloballyAdditionalStyles(); + $additionalStyles = Helpers::getConfigOutputCssGloballyAdditionalStyles(); $additionalStylesOutput = $additionalStyles ? \esc_html(\implode(";\n", $additionalStyles)) : ''; - $selector = Components::getConfigOutputCssSelectorName(); + $selector = Helpers::getConfigOutputCssSelectorName(); return ""; } @@ -368,7 +365,7 @@ private static function getCssVariablesTypeDefault(string $name, array $data, ar // Prepare output for manual variables. $finalManualOutput = $manual ? "\n .{$name}{$uniqueSelector}{\n{$manual}\n}" : ''; - if (Components::getConfigOutputCssOptimize()) { + if (Helpers::getConfigOutputCssOptimize()) { $output = \str_replace(["\n", "\r"], '', $output); $finalManualOutput = \str_replace(["\n", "\r"], '', $finalManualOutput); } @@ -452,8 +449,8 @@ private static function globalInner(array $itemValues, string $itemKey): string $output = ''; foreach ($itemValues as $key => $value) { - $key = Components::camelToKebabCase((string)$key); - $itemKey = Components::camelToKebabCase((string)$itemKey); + $key = Helpers::camelToKebabCase((string)$key); + $itemKey = Helpers::camelToKebabCase((string)$itemKey); switch ($itemKey) { case 'colors': @@ -609,7 +606,7 @@ private static function setVariablesToBreakpoints(array $attributes, array $vari { foreach ($variables as $variableName => $variableValue) { // Constant for attributes set value (in db or default). - $attributeValue = $attributes[Components::getAttrKey($variableName, $attributes, $manifest)] ?? ''; + $attributeValue = $attributes[Helpers::getAttrKey($variableName, $attributes, $manifest)] ?? ''; // Make sure this works correctly for attributes which are toggles (booleans). if (\is_bool($attributeValue)) { @@ -617,7 +614,7 @@ private static function setVariablesToBreakpoints(array $attributes, array $vari } // If type default or value. - if (!Components::arrayIsList($variableValue)) { + if (!Helpers::arrayIsList($variableValue)) { $variableValue = $variableValue[$attributeValue] ?? []; } @@ -760,14 +757,14 @@ private static function variablesInner(array $variables, $attributeValue): array $output = []; // Bailout if provided list is not an object. - if (Components::arrayIsList($variables)) { + if (Helpers::arrayIsList($variables)) { return $output; } // Iterate each attribute and make corrections. foreach ($variables as $variableKey => $variableValue) { // Convert to correct case. - $internalKey = Components::camelToKebabCase($variableKey); + $internalKey = Helpers::camelToKebabCase($variableKey); // If value contains magic variable swap that variable with original attribute value. if (\strpos($variableValue, '%value%') !== false) { diff --git a/src/Helpers/DeprecatedTrait.php b/src/Helpers/DeprecatedTrait.php new file mode 100644 index 000000000..e1b6d6a14 --- /dev/null +++ b/src/Helpers/DeprecatedTrait.php @@ -0,0 +1,99 @@ + $attributes Attributes that will be passed to partial. + * @param string $partialFolderName Partial folder name. + * + * @throws InvalidPath If the file is missing. + * + * @deprecated 8.0.0 Use Helpers::render() instead. This method will be removed in the next major release. + * + * @return string Partial html. + */ + public static function renderPartial( + string $type, + string $parent, + string $name, + array $attributes = [], + string $partialFolderName = 'partials' + ): string { + $sep = \DIRECTORY_SEPARATOR; + + // If no extension is provided use php. + if (\strpos($name, '.php') === false) { + $name = "{$name}.php"; + } + + $partialPath = "{$parent}{$sep}{$partialFolderName}{$sep}{$name}"; + + // Detect folder based on the name. + switch ($type) { + case 'block': + case 'blocks': + case 'custom': + $path = Helpers::getProjectPaths('blocksDestinationCustom', $partialPath); + break; + case 'component': + case 'components': + $path = Helpers::getProjectPaths('blocksDestinationComponents', $partialPath); + break; + case 'variation': + case 'variations': + $path = Helpers::getProjectPaths('blocksDestinationVariations', $partialPath); + break; + case 'wrapper': + $path = Helpers::getProjectPaths('blocksDestinationWrapper', $partialPath); + break; + default: + $path = Helpers::getProjectPaths('root', $partialPath); + break; + } + + // Bailout if file is missing. + if (!\file_exists($path)) { + throw InvalidPath::missingFileException($path); + } + + \ob_start(); + + include $path; + + return \trim((string) \ob_get_clean()); + } + + /** + * Get manifest json by path and name (old method). + * + * @param string $path Absolute path. + * + * @deprecated 8.0.0 Use Components::getManifestByDir() instead. This method will be removed in the next major release. + * + * @return array + */ + public static function getManifest(string $path): array + { + return self::getManifestByDir($path); + } +} diff --git a/src/Helpers/ErrorLoggerTrait.php b/src/Helpers/ErrorLoggerTrait.php deleted file mode 100644 index d5ae6ef54..000000000 --- a/src/Helpers/ErrorLoggerTrait.php +++ /dev/null @@ -1,53 +0,0 @@ - $code, - 'status' => $status, - 'message' => $msg, - 'data' => $data, - ]; - - if ($code >= 200 && $code < 300) { - \ob_start(); - \wp_send_json_success($output, $code); - $response = \ob_get_clean(); - } else { - \ob_start(); - \wp_send_json_error(new WP_Error($output), $code); - $response = \ob_get_clean(); - } - - return \rest_ensure_response($response); - } -} diff --git a/src/Helpers/Helpers.php b/src/Helpers/Helpers.php new file mode 100644 index 000000000..781d9e4db --- /dev/null +++ b/src/Helpers/Helpers.php @@ -0,0 +1,445 @@ + + */ + private const PROJECT_RENDER_ALLOWED_NAMES = [ + 'blocks', + 'components', + 'variations', + 'wrapper', + 'root', + 'srcDestination', + 'themeRoot', + 'pluginRoot', + 'blocksDestination', + ]; + + /** + * Renders a components and (optionally) passes some attributes to it. + * + * @param string $renderName The name of the component to render. + * @param array $renderAttributes The attributes to pass to the component. + * @param string $renderPathName The path name where the component is located. + * @param bool $renderUseComponentDefaults Should we use the default attributes from the component. + * @param string $renderPrefixPath The prefix path to the component. + * @param string $renderContent The content to pass to the component. + * + * @throws InvalidPath If the file is missing. + * + * @return string + */ + public static function render( + string $renderName, + array $renderAttributes = [], + string $renderPathName = 'components', + bool $renderUseComponentDefaults = false, + string $renderPrefixPath = '', + string $renderContent = '' + ): string { + if (empty($renderPathName)) { + $renderPathName = 'components'; + } + + if ($renderName === 'wrapper') { + $renderPathName = 'blocksDestination'; + } + + if (!isset(\array_flip(self::PROJECT_RENDER_ALLOWED_NAMES)[$renderPathName])) { + throw InvalidPath::wrongOrNotAllowedParentPathException($renderPathName, \implode(', ', self::PROJECT_RENDER_ALLOWED_NAMES)); + } + + $renderPrefix = $renderName; + + if (!empty($renderPrefixPath)) { + $renderPrefix = $renderPrefixPath; + } + + $renderPath = self::joinPaths([Helpers::getProjectPaths($renderPathName), $renderPrefix, "{$renderName}.php"]); + + if (!\file_exists($renderPath)) { + throw InvalidPath::missingFileException($renderPath); + } + + // Merge default attributes with the component attributes. + if ($renderUseComponentDefaults) { + $renderAttributes = Helpers::getDefaultRenderAttributes(Helpers::getComponent($renderName), $renderAttributes); + } + + \ob_start(); + + // Allowed variables are $attributes, $renderAttributes, $renderContent, $renderPath. + $attributes = $renderAttributes; + + unset( + $renderName, + $renderPathName, + $renderUseComponentDefaults, + $renderPrefixPath, + $renderPrefix + ); + + include $renderPath; + + unset( + $renderAttributes, + $attributes, + $renderContent, + $renderPath + ); + + return \trim((string) \ob_get_clean()); + } + + /** + * Get manifest json by path and name. + * + * @param string $path Absolute path to. + * + * @throws InvalidManifest If the manifest is not allowed. + * + * @return array + */ + public static function getManifestByDir(string $path): array + { + $sep = \DIRECTORY_SEPARATOR; + $root = Helpers::getProjectPaths('srcDestination'); + $newPath = \str_replace($root, '', $path); + $newPath = \explode($sep, $newPath); + + if (!isset($newPath[0]) && $newPath[0] !== 'Blocks') { + throw InvalidManifest::notAllowedManifestPathException($path); + } + + if (!isset($newPath[1])) { + throw InvalidManifest::notAllowedManifestPathException($path); + } + + switch ($newPath[1]) { + case 'wrapper': + return Helpers::getWrapper(); + case 'components': + return Helpers::getComponent(\end($newPath)); + case 'custom': + return Helpers::getBlock(\end($newPath)); + default: + throw InvalidManifest::missingManifestException($path); + } + } + + /** + * Internal helper for getting all project paths for easy mocking in tests. + * + * @param string $type Type fo path to return. + * @param string $suffix Additional suffix path to add. + * @param string $prefix Additional prefix instead of dirname path. + * + * @return string + */ + public static function getProjectPaths(string $type = '', string $suffix = '', string $prefix = ''): string + { + $path = ''; + $internalPrefix = \dirname(__FILE__, 6); + + if (\getenv('ES_TEST')) { + $internalPrefix = \dirname(__FILE__, 3); + } + + $flibsPath = ["node_modules", "@eightshift", "frontend-libs", "blocks", "init"]; + $fPLibsPath = ["node_modules", "@eightshift", "frontend-libs-private", "blocks", "init"]; + $libsPath = ["vendor", "infinum", "eightshift-libs"]; + $libsPrefixedPath = ["vendor-prefixed", "infinum", "eightshift-libs"]; + $testsDataPath = ["tests", "data"]; + $srcPath = "src"; + $blocksPath = [$srcPath, "Blocks"]; + $assetsPath = "assets"; + $cliOutputPath = "cliOutput"; + + $name = ''; + + switch ($type) { + case 'projectRoot': + $internalPrefix = \dirname(__FILE__, 9); + + if (\getenv('ES_TEST')) { + $internalPrefix = \dirname(__FILE__, 3); + } + break; + case 'testsData': + if (\getenv('ES_TEST')) { + $internalPrefix = \dirname(__FILE__, 3); + $path = self::joinPaths([...$testsDataPath]); + } + break; + case 'srcDestination': + $path = $srcPath; + + if (\getenv('ES_TEST')) { + $internalPrefix = \dirname(__FILE__, 3); + $path = self::joinPaths([$cliOutputPath, $srcPath]); + } + break; + case 'cliOutput': + case 'root': + case 'themeRoot': + case 'pluginRoot': + if (\getenv('ES_TEST')) { + $internalPrefix = \dirname(__FILE__, 3); + $path = $cliOutputPath; + } + break; + case 'wpContent': + $internalPrefix = \dirname(__FILE__, 8); + + if (\getenv('ES_TEST')) { + $internalPrefix = \dirname(__FILE__, 3); + } + break; + case 'libs': + $path = self::joinPaths($libsPath); + + if (\getenv('ES_TEST')) { + $path = ''; + } + break; + case 'libsPrefixed': + $path = self::joinPaths($libsPrefixedPath); + + if (\getenv('ES_TEST')) { + $path = ''; + } + break; + case 'blocksGlobalAssetsSource': + $path = self::joinPaths([...$flibsPath, $assetsPath]); + + if (\getenv('ES_TEST')) { + $path = self::joinPaths([...$testsDataPath, $assetsPath]); + } + break; + case 'blocksAssetsSource': + $path = self::joinPaths([...$flibsPath, ...$blocksPath, $assetsPath]); + + if (\getenv('ES_TEST')) { + $path = self::joinPaths([...$testsDataPath, ...$blocksPath, $assetsPath]); + } + break; + case 'blocksSource': + $path = self::joinPaths([...$flibsPath, ...$blocksPath]); + + if (\getenv('ES_TEST')) { + $path = self::joinPaths([...$testsDataPath, ...$blocksPath]); + } + break; + case 'blocksPrivateSource': + $path = self::joinPaths([...$fPLibsPath, ...$blocksPath]); + + if (\getenv('ES_TEST')) { + $path = self::joinPaths([...$testsDataPath, ...$blocksPath]); + } + break; + case 'block': + case 'blocks': + case 'custom': + case 'blocksDestinationCustom': + case 'blocksSourceCustom': + case 'blocksPrivateSourceCustom': + $name = 'custom'; + break; + case 'component': + case 'components': + case 'blocksDestinationComponents': + case 'blocksSourceComponents': + case 'blocksPrivateSourceComponents': + $name = 'components'; + break; + case 'variation': + case 'variations': + case 'blocksDestinationVariations': + case 'blocksSourceVariations': + case 'blocksPrivateSourceVariations': + $name = 'variations'; + break; + case 'wrapper': + case 'blocksDestinationWrapper': + case 'blocksSourceWrapper': + $name = 'wrapper'; + break; + case 'blocksGlobalAssetsDestination': + $path = self::joinPaths([$assetsPath]); + + if (\getenv('ES_TEST')) { + $path = self::joinPaths([$cliOutputPath, $assetsPath]); + } + break; + case 'blocksAssetsDestination': + $path = self::joinPaths([...$blocksPath, $assetsPath]); + + if (\getenv('ES_TEST')) { + $path = self::joinPaths([$cliOutputPath, ...$blocksPath, $assetsPath]); + } + break; + case 'blocksDestination': + $path = self::joinPaths($blocksPath); + + if (\getenv('ES_TEST')) { + $path = self::joinPaths([$cliOutputPath, ...$blocksPath]); + } + break; + } + + switch ($type) { + case 'blocksSourceCustom': + case 'blocksSourceComponents': + case 'blocksSourceVariations': + case 'blocksSourceWrapper': + $path = self::joinPaths([...$flibsPath, ...$blocksPath, $name]); + + if (\getenv('ES_TEST')) { + $path = self::joinPaths([...$testsDataPath, ...$blocksPath, $name]); + } + break; + + case 'blocksPrivateSourceCustom': + case 'blocksPrivateSourceComponents': + case 'blocksPrivateSourceVariations': + $path = self::joinPaths([...$fPLibsPath, ...$blocksPath, $name]); + + if (\getenv('ES_TEST')) { + $path = self::joinPaths([...$testsDataPath, ...$blocksPath, $name]); + } + break; + + case 'block': + case 'blocks': + case 'custom': + case 'component': + case 'components': + case 'variation': + case 'variations': + case 'wrapper': + case 'blocksDestinationCustom': + case 'blocksDestinationComponents': + case 'blocksDestinationVariations': + case 'blocksDestinationWrapper': + $path = self::joinPaths([...$blocksPath, $name]); + + if (\getenv('ES_TEST')) { + $path = self::joinPaths([$cliOutputPath, ...$blocksPath, $name]); + } + break; + } + + if (!$prefix) { + $prefix = $internalPrefix; + } + + $path = self::joinPaths([$prefix, $path, $suffix]); + + return $path; + } + + /** + * Paths join + * + * @param array $paths Paths to join. + * + * @return string + */ + public static function joinPaths(array $paths): string + { + $sep = \DIRECTORY_SEPARATOR; + + $paths = \array_filter( + \array_map( + static function ($path) use ($sep) { + return \trim($path, $sep); + }, + $paths + ) + ); + + $path = \implode($sep, $paths); + $path = "{$sep}{$path}"; + + if (!\pathinfo($path, \PATHINFO_EXTENSION)) { + $path = "{$path}{$sep}"; + } + + return $path; + } +} diff --git a/src/Helpers/LabelGeneratorTrait.php b/src/Helpers/LabelGeneratorTrait.php deleted file mode 100644 index 2da94bbd4..000000000 --- a/src/Helpers/LabelGeneratorTrait.php +++ /dev/null @@ -1,114 +0,0 @@ - $nouns Array of nouns to use for the labels. - * - * @return string[] array Array of labels. - * @throws InvalidNouns Invalid nouns exception. - */ - protected function getGeneratedLabels(array $nouns): array - { - $requiredNouns = [ - 'upper case singular name', - 'lower case singular name', - 'upper case plural name', - 'lower case plural name', - ]; - - if (\count($nouns) !== \count($requiredNouns)) { - throw InvalidNouns::fromKey($requiredNouns[\count($nouns)]); - } - - $labelTemplates = [ - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'name' => esc_html_x('%3$s', 'Post Type General Name', 'eightshift-libs'), /* phpcs:disable */ - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'singular_name' => esc_html_x('%1$s', 'Post Type Singular Name', 'eightshift-libs'), /* phpcs:disable */ - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'menu_name' => \esc_html__('%3$s', 'eightshift-libs'), /* phpcs:disable */ - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'name_admin_bar' => \esc_html__('%1$s', 'eightshift-libs'), /* phpcs:disable */ - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'archives' => \esc_html__('%1$s Archives', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'attributes' => \esc_html__('%1$s Attributes', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'parent_item_colon' => \esc_html__('Parent %1$s:', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'all_items' => \esc_html__('All %3$s', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'add_new_item' => \esc_html__('Add New %1$s', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'add_new' => \esc_html__('Add New', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'new_item' => \esc_html__('New %1$s', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'edit_item' => \esc_html__('Edit %1$s', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'update_item' => \esc_html__('Update %1$s', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'view_item' => \esc_html__('View %1$s', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'view_items' => \esc_html__('View %3$s', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'search_items' => \esc_html__('Search %1$s', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'not_found' => \esc_html__('Not found', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'not_found_in_trash' => \esc_html__('Not found in Trash', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'featured_image' => \esc_html__('Featured Image', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'set_featured_image' => \esc_html__('Set featured image', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'remove_featured_image' => \esc_html__('Remove featured image', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'use_featured_image' => \esc_html__('Use as featured image', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'insert_into_item' => \esc_html__('Insert into %2$s', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'uploaded_to_this_item' => \esc_html__('Uploaded to this %2$s', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'items_list' => \esc_html__('%3$s list', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'items_list_navigation' => \esc_html__('%3$s list navigation', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'filter_items_list' => \esc_html__('Filter %4$s list', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'item_link' => \esc_html__('%1$s Link', 'eightshift-libs'), - /* Translators: %1$s uc singular, %2$s lc singular, %3$s uc plural, %4$s lc plural. */ - 'item_link_description' => \esc_html__('A link to a %2$s', 'eightshift-libs'), - 'filter_by_date' => \esc_html__('Filter by date', 'eightshift-libs'), - ]; - - return \array_map( - static function ($label) use ($nouns) { - return \sprintf( - $label, - ...$nouns - ); - }, - $labelTemplates - ); - } -} diff --git a/src/Helpers/ObjectHelperTrait.php b/src/Helpers/ObjectHelperTrait.php index 1fb6d7303..0098d856c 100644 --- a/src/Helpers/ObjectHelperTrait.php +++ b/src/Helpers/ObjectHelperTrait.php @@ -13,6 +13,8 @@ use DOMDocument; use EightshiftLibs\Exception\InvalidManifest; +use RecursiveArrayIterator; +use RecursiveIteratorIterator; /** * Class Object Helper @@ -71,6 +73,29 @@ function ($a) use (&$output) { return $output; } + /** + * Find array value by key in recursive array. + * + * @param array $array Array to find. + * @param string $needle Key name to find. + * + * @return array + */ + public static function recursiveArrayFind(array $array, string $needle): array + { + $iterator = new RecursiveArrayIterator($array); + $recursive = new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::SELF_FIRST); + $aHitList = []; + + foreach ($recursive as $key => $value) { + if ($key === $needle) { + \array_push($aHitList, $value); + } + } + + return $aHitList; + } + /** * Sanitize all values in an array. * @@ -132,6 +157,18 @@ public static function camelToKebabCase(string $convert): string return \str_replace('--', '-', $output); } + /** + * Convert camel to snake case + * + * @param string $input Name to change. + * + * @return string + */ + public static function camelToSnakeCase($input): string + { + return \strtolower((string) \preg_replace('/(?get('Version'); + } + + /** + * Get the theme name. + * + * @return string + */ + public static function getThemeName(): string + { + return \wp_get_theme()->get('Name'); + } + + /** + * Get the theme text domain. + * + * @return string + */ + public static function getThemeTextDomain(): string + { + return \wp_get_theme()->get('TextDomain'); + } + + /** + * Get the plugin details. + * + * @return array + */ + protected static function getPluginDetails(): array + { + if (!\function_exists('get_plugin_data')) { + require_once(\ABSPATH . 'wp-admin/includes/plugin.php'); + } + + $path = Helpers::getProjectPaths('pluginRoot'); + + $name = \basename($path); + + return \get_plugin_data("{$path}{$name}.php"); + } +} diff --git a/src/Helpers/SelectorsTrait.php b/src/Helpers/SelectorsTrait.php index e50d7da3f..59dab99ba 100644 --- a/src/Helpers/SelectorsTrait.php +++ b/src/Helpers/SelectorsTrait.php @@ -52,7 +52,7 @@ public static function selector($condition, string $block, string $element = '', * Create responsive selectors used for responsive attributes. * * Example: - * Components::responsiveSelectors($attributes['width'], 'width', $block_class); + * Helpers::responsiveSelectors($attributes['width'], 'width', $block_class); * * Output: * block-column__width-large--4 diff --git a/src/Helpers/StoreTrait.php b/src/Helpers/StoreBlocksTrait.php similarity index 54% rename from src/Helpers/StoreTrait.php rename to src/Helpers/StoreBlocksTrait.php index 7ca13a8b8..fb0622f83 100644 --- a/src/Helpers/StoreTrait.php +++ b/src/Helpers/StoreBlocksTrait.php @@ -1,7 +1,7 @@ */ public static $defaultState = [ - 'blocks' => [], - 'components' => [], - 'config' => [ + AbstractManifestCache::BLOCKS_KEY => [], + AbstractManifestCache::COMPONENTS_KEY => [], + AbstractManifestCache::CONFIG_KEY => [ 'outputCssGlobally' => false, 'outputCssOptimize' => false, 'outputCssSelectorName' => 'esCssVariables', 'outputCssGloballyAdditionalStyles' => [], 'useWrapper' => true, + 'useComponents' => true, + 'useBlocks' => true, + 'useVariations' => true, ], - 'wrapper' => [], - 'variations' => [], - 'settings' => [], - 'styles' => [], - 'paths' => [], + AbstractManifestCache::WRAPPER_KEY => [], + AbstractManifestCache::VARIATIONS_KEY => [], + AbstractManifestCache::SETTINGS_KEY => [], + AbstractManifestCache::STYLES_KEY => [], + AbstractManifestCache::ASSETS_KEY => [], ]; /** @@ -44,7 +50,7 @@ trait StoreTrait */ public static function getStoreName(): string { - return \basename(Components::getProjectPaths('root')); + return \basename(Helpers::getProjectPaths('root')); } /** @@ -87,37 +93,46 @@ public static function setBlocks(array $blocks): void global $esBlocks; if (self::getStore()) { - $esBlocks[self::getStoreName()]['blocks'] = $blocks; + $esBlocks[self::getStoreName()][AbstractManifestCache::BLOCKS_KEY] = $blocks; } } /** * Get blocks details. * + * @throws InvalidBlock If blocks are missing. + * * @return array */ public static function getBlocks(): array { - return self::getStore()['blocks'] ?? []; + $output = self::getStore()[AbstractManifestCache::BLOCKS_KEY] ?? []; + + if (!$output) { + throw InvalidBlock::missingItemException('project', 'blocks'); + } + + return $output; } /** * Get block details. * + * @throws InvalidBlock If block is missing. + * * @param string $block Block name to get. * * @return array */ public static function getBlock(string $block): array { - $blocks = \array_filter( - self::getBlocks(), - static function ($item) use ($block) { - return $item['blockName'] === $block; - } - ); + $output = self::getBlocks()[$block] ?? []; - return \reset($blocks) ?: []; // phpcs:ignore WordPress.PHP.DisallowShortTernary.Found + if (!$output) { + throw InvalidBlock::missingItemException($block, 'block'); + } + + return $output; } /** @@ -132,36 +147,46 @@ public static function setComponents(array $components): void global $esBlocks; if (self::getStore()) { - $esBlocks[self::getStoreName()]['components'] = $components; + $esBlocks[self::getStoreName()][AbstractManifestCache::COMPONENTS_KEY] = $components; } } /** * Get components details. * + * @throws InvalidBlock If components are missing. + * * @return array */ public static function getComponents(): array { - return self::getStore()['components'] ?? []; + $output = self::getStore()[AbstractManifestCache::COMPONENTS_KEY] ?? []; + + if (!$output) { + throw InvalidBlock::missingItemException('project', 'components'); + } + + return $output; } /** * Get component details. * * @param string $component Componennt name to get. + * + * @throws InvalidBlock If component is missing. + * * @return array */ public static function getComponent(string $component): array { - $components = \array_filter( - self::getComponents(), - static function ($item) use ($component) { - return $item['componentName'] === $component; - } - ); + $output = self::getComponents()[$component] ?? []; + + if (!$output) { + throw InvalidBlock::missingItemException($component, 'component'); + } - return \reset($components) ?: []; // phpcs:ignore WordPress.PHP.DisallowShortTernary.Found + return $output; } /** @@ -176,36 +201,46 @@ public static function setVariations(array $variations): void global $esBlocks; if (self::getStore()) { - $esBlocks[self::getStoreName()]['variations'] = $variations; + $esBlocks[self::getStoreName()][AbstractManifestCache::VARIATIONS_KEY] = $variations; } } /** * Get variations details. * + * @throws InvalidBlock If variations are missing. + * * @return array */ public static function getVariations(): array { - return self::getStore()['variations'] ?? []; + $output = self::getStore()[AbstractManifestCache::VARIATIONS_KEY] ?? []; + + if (!$output) { + throw InvalidBlock::missingItemException('project', 'variations'); + } + + return $output; } /** * Get variation details. * * @param string $variation Variation name to get. + * + * @throws InvalidBlock If variation is missing. + * * @return array */ public static function getVariation(string $variation): array { - $variations = \array_filter( - self::getVariations(), - static function ($item) use ($variation) { - return $item['name'] === $variation; - } - ); + $output = self::getVariations()[$variation] ?? []; + + if (!$output) { + throw InvalidBlock::missingItemException($variation, 'variation'); + } - return \reset($variations) ?: []; // phpcs:ignore WordPress.PHP.DisallowShortTernary.Found + return $output; } /** @@ -215,32 +250,47 @@ static function ($item) use ($variation) { */ public static function setConfigFlags(): void { - $config = self::getSettings()['config'] ?? []; + $config = self::getSettings()[AbstractManifestCache::CONFIG_KEY] ?? []; if ($config) { // outputCssGlobally. if (isset($config['outputCssGlobally']) && \gettype($config['outputCssGlobally']) === 'boolean') { - Components::setConfigOutputCssGlobally($config['outputCssGlobally']); + Helpers::setConfigOutputCssGlobally($config['outputCssGlobally']); } // outputCssOptimize. if (isset($config['outputCssOptimize']) && \gettype($config['outputCssOptimize']) === 'boolean') { - Components::setConfigOutputCssOptimize($config['outputCssOptimize']); + Helpers::setConfigOutputCssOptimize($config['outputCssOptimize']); } // outputCssSelectorName. if (isset($config['outputCssSelectorName']) && \gettype($config['outputCssSelectorName']) === 'string') { - Components::setConfigOutputCssSelectorName($config['outputCssSelectorName']); + Helpers::setConfigOutputCssSelectorName($config['outputCssSelectorName']); } // outputCssGloballyAdditionalStyles. if (isset($config['outputCssGloballyAdditionalStyles']) && \gettype($config['outputCssGloballyAdditionalStyles']) === 'array') { - Components::setConfigOutputCssGloballyAdditionalStyles($config['outputCssGloballyAdditionalStyles']); + Helpers::setConfigOutputCssGloballyAdditionalStyles($config['outputCssGloballyAdditionalStyles']); } // useWrapper. if (isset($config['useWrapper']) && \gettype($config['useWrapper']) === 'boolean') { - Components::setConfigUseWrapper($config['useWrapper']); + Helpers::setConfigUseWrapper($config['useWrapper']); + } + + // useComponents. + if (isset($config['useComponents']) && \gettype($config['useComponents']) === 'boolean') { + Helpers::setConfigUseComponents($config['useComponents']); + } + + // useBlocks. + if (isset($config['useBlocks']) && \gettype($config['useBlocks']) === 'boolean') { + Helpers::setConfigUseBlocks($config['useBlocks']); + } + + // useVariations. + if (isset($config['useVariations']) && \gettype($config['useVariations']) === 'boolean') { + Helpers::setConfigUseVariations($config['useVariations']); } } } @@ -252,7 +302,7 @@ public static function setConfigFlags(): void */ public static function getConfig(): array { - return self::getStore()['config'] ?? []; + return self::getStore()[AbstractManifestCache::CONFIG_KEY] ?? []; } /** @@ -267,7 +317,7 @@ public static function setConfigOutputCssGlobally(bool $config): void global $esBlocks; if (self::getStore() && self::getConfig()) { - $esBlocks[self::getStoreName()]['config']['outputCssGlobally'] = $config; + $esBlocks[self::getStoreName()][AbstractManifestCache::CONFIG_KEY]['outputCssGlobally'] = $config; } } @@ -278,7 +328,7 @@ public static function setConfigOutputCssGlobally(bool $config): void */ public static function getConfigOutputCssGlobally(): bool { - return self::getConfig()['outputCssGlobally'] ?? self::$defaultState['config']['outputCssGlobally']; + return self::getConfig()['outputCssGlobally'] ?? self::$defaultState[AbstractManifestCache::CONFIG_KEY]['outputCssGlobally']; } /** @@ -293,7 +343,7 @@ public static function setConfigOutputCssOptimize(bool $config): void global $esBlocks; if (self::getStore() && self::getConfig()) { - $esBlocks[self::getStoreName()]['config']['outputCssOptimize'] = $config; + $esBlocks[self::getStoreName()][AbstractManifestCache::CONFIG_KEY]['outputCssOptimize'] = $config; } } @@ -304,7 +354,7 @@ public static function setConfigOutputCssOptimize(bool $config): void */ public static function getConfigOutputCssOptimize(): bool { - return self::getConfig()['outputCssOptimize'] ?? self::$defaultState['config']['outputCssOptimize']; + return self::getConfig()['outputCssOptimize'] ?? self::$defaultState[AbstractManifestCache::CONFIG_KEY]['outputCssOptimize']; } /** @@ -319,7 +369,7 @@ public static function setConfigOutputCssSelectorName(string $config): void global $esBlocks; if (self::getStore() && self::getConfig()) { - $esBlocks[self::getStoreName()]['config']['outputCssSelectorName'] = $config; + $esBlocks[self::getStoreName()][AbstractManifestCache::CONFIG_KEY]['outputCssSelectorName'] = $config; } } @@ -330,7 +380,7 @@ public static function setConfigOutputCssSelectorName(string $config): void */ public static function getConfigOutputCssSelectorName(): string { - return self::getConfig()['outputCssSelectorName'] ?? self::$defaultState['config']['outputCssSelectorName']; + return self::getConfig()['outputCssSelectorName'] ?? self::$defaultState[AbstractManifestCache::CONFIG_KEY]['outputCssSelectorName']; } /** @@ -345,7 +395,7 @@ public static function setConfigOutputCssGloballyAdditionalStyles(array $config) global $esBlocks; if (self::getStore() && self::getConfig()) { - $esBlocks[self::getStoreName()]['config']['outputCssGloballyAdditionalStyles'] = $config; + $esBlocks[self::getStoreName()][AbstractManifestCache::CONFIG_KEY]['outputCssGloballyAdditionalStyles'] = $config; } } @@ -356,7 +406,7 @@ public static function setConfigOutputCssGloballyAdditionalStyles(array $config) */ public static function getConfigOutputCssGloballyAdditionalStyles(): array { - return self::getConfig()['outputCssGloballyAdditionalStyles'] ?? self::$defaultState['config']['outputCssGloballyAdditionalStyles']; + return self::getConfig()['outputCssGloballyAdditionalStyles'] ?? self::$defaultState[AbstractManifestCache::CONFIG_KEY]['outputCssGloballyAdditionalStyles']; } /** @@ -371,7 +421,7 @@ public static function setConfigUseWrapper(bool $config): void global $esBlocks; if (self::getStore() && self::getConfig()) { - $esBlocks[self::getStoreName()]['config']['useWrapper'] = $config; + $esBlocks[self::getStoreName()][AbstractManifestCache::CONFIG_KEY]['useWrapper'] = $config; } } @@ -382,7 +432,85 @@ public static function setConfigUseWrapper(bool $config): void */ public static function getConfigUseWrapper(): bool { - return self::getConfig()['useWrapper'] ?? self::$defaultState['config']['useWrapper']; + return self::getConfig()['useWrapper'] ?? self::$defaultState[AbstractManifestCache::CONFIG_KEY]['useWrapper']; + } + + /** + * Set global config value for use components. + * + * @param bool $config Config value. + * + * @return void + */ + public static function setConfigUseComponents(bool $config): void + { + global $esBlocks; + + if (self::getStore() && self::getConfig()) { + $esBlocks[self::getStoreName()][AbstractManifestCache::CONFIG_KEY]['useComponents'] = $config; + } + } + + /** + * Get global config value for use components. + * + * @return bool + */ + public static function getConfigUseComponents(): bool + { + return self::getConfig()['useComponents'] ?? self::$defaultState[AbstractManifestCache::CONFIG_KEY]['useComponents']; + } + + /** + * Set global config value for use blocks. + * + * @param bool $config Config value. + * + * @return void + */ + public static function setConfigUseBlocks(bool $config): void + { + global $esBlocks; + + if (self::getStore() && self::getConfig()) { + $esBlocks[self::getStoreName()][AbstractManifestCache::CONFIG_KEY]['useBlocks'] = $config; + } + } + + /** + * Get global config value for use blocks. + * + * @return bool + */ + public static function getConfigUseBlocks(): bool + { + return self::getConfig()['useBlocks'] ?? self::$defaultState[AbstractManifestCache::CONFIG_KEY]['useBlocks'] ?? false; + } + + /** + * Set global config value for use variations. + * + * @param bool $config Config value. + * + * @return void + */ + public static function setConfigUseVariations(bool $config): void + { + global $esBlocks; + + if (self::getStore() && self::getConfig()) { + $esBlocks[self::getStoreName()][AbstractManifestCache::CONFIG_KEY]['useVariations'] = $config; + } + } + + /** + * Get global config value for use variations. + * + * @return bool + */ + public static function getConfigUseVariations(): bool + { + return self::getConfig()['useVariations'] ?? self::$defaultState[AbstractManifestCache::CONFIG_KEY]['useVariations']; } /** @@ -397,18 +525,26 @@ public static function setWrapper(array $wrapper): void global $esBlocks; if (self::getStore()) { - $esBlocks[self::getStoreName()]['wrapper'] = $wrapper; + $esBlocks[self::getStoreName()][AbstractManifestCache::WRAPPER_KEY] = $wrapper; } } /** * Get wrapper details. * + * @throws InvalidBlock If wrapper is missing. + * * @return array */ public static function getWrapper(): array { - return self::getStore()['wrapper'] ?? []; + $output = self::getStore()[AbstractManifestCache::WRAPPER_KEY] ?? []; + + if (!$output) { + throw InvalidBlock::missingItemException('blocks wrapper', 'component'); + } + + return $output; } /** @@ -433,18 +569,26 @@ public static function setSettings(array $settings): void global $esBlocks; if (self::getStore()) { - $esBlocks[self::getStoreName()]['settings'] = $settings; + $esBlocks[self::getStoreName()][AbstractManifestCache::SETTINGS_KEY] = $settings; } } /** * Get global settings details. * + * @throws InvalidBlock If settings are missing. + * * @return array */ public static function getSettings(): array { - return self::getStore()['settings'] ?? []; + $output = self::getStore()[AbstractManifestCache::SETTINGS_KEY] ?? []; + + if (!$output) { + throw InvalidBlock::missingItemException('project', 'global settings'); + } + + return $output; } /** @@ -509,7 +653,7 @@ public static function setSettingsGlobalVariablesBreakpoints(array $breakpoints) global $esBlocks; if (self::getStore()) { - $esBlocks[self::getStoreName()]['settings']['globalVariables']['breakpoints'] = $breakpoints; + $esBlocks[self::getStoreName()][AbstractManifestCache::SETTINGS_KEY]['globalVariables']['breakpoints'] = $breakpoints; } } @@ -545,7 +689,7 @@ public static function setStyles(array $styles): void global $esBlocks; if (self::getStore()) { - $esBlocks[self::getStoreName()]['styles'] = $styles; + $esBlocks[self::getStoreName()][AbstractManifestCache::STYLES_KEY] = $styles; } } @@ -561,7 +705,7 @@ public static function setStyle(array $style): void global $esBlocks; if (self::getStore()) { - $esBlocks[self::getStoreName()]['styles'][] = $style; + $esBlocks[self::getStoreName()][AbstractManifestCache::STYLES_KEY][] = $style; } } @@ -572,32 +716,60 @@ public static function setStyle(array $style): void */ public static function getStyles(): array { - return self::getStore()['styles'] ?? []; + return self::getStore()[AbstractManifestCache::STYLES_KEY] ?? []; } /** - * Set paths details. + * Set assets details. + * + * @param array $assets Assets to store. * * @return void */ - public static function setPaths(): void + public static function setAssets(array $assets): void { global $esBlocks; if (self::getStore()) { - foreach (Components::PROJECT_PATHS as $value) { - $esBlocks[self::getStoreName()]['paths'][$value] = Components::getProjectPaths($value); - } + $esBlocks[self::getStoreName()][AbstractManifestCache::ASSETS_KEY] = $assets; } } /** - * Get paths details. + * Get assets details. + * + * @throws InvalidBlock If assets are missing. * * @return array */ - public static function getPaths(): array + public static function getAssets(): array { - return self::getStore()['paths'] ?? []; + $output = self::getStore()[AbstractManifestCache::ASSETS_KEY] ?? []; + + if (!$output) { + throw InvalidBlock::missingItemException('public', 'assets'); + } + + return $output; + } + + /** + * Get asset details. + * + * @param string $asset Asset name to get. + * + * @throws InvalidBlock If asset is missing. + * + * @return string + */ + public static function getAsset(string $asset): string + { + $output = self::getAssets()[$asset] ?? ''; + + if (!$output) { + throw InvalidBlock::missingItemException($asset, 'public asset'); + } + + return $output; } } diff --git a/src/I18n/I18nExample.php b/src/I18n/I18nExample.php index c939c7f50..3c4a77c6a 100644 --- a/src/I18n/I18nExample.php +++ b/src/I18n/I18nExample.php @@ -11,6 +11,7 @@ namespace EightshiftBoilerplate\I18n; use EightshiftBoilerplate\Config\Config; +use EightshiftLibs\Helpers\Helpers; use EightshiftLibs\Services\ServiceInterface; /** @@ -38,9 +39,10 @@ public function register(): void */ public function loadThemeTextdomain(): void { + $sep = \DIRECTORY_SEPARATOR; \load_theme_textdomain( Config::getProjectName(), - Config::getProjectPath('src/I18n/languages') + Helpers::getProjectPaths('srcDestination', "I18n{$sep}languages") ); } @@ -60,7 +62,7 @@ public function setScriptTranslations(): void \wp_set_script_translations( $handle, Config::getProjectName(), - Config::getProjectPath('src/I18n/languages') + Helpers::getProjectPaths('srcDestination', "I18n{$sep}languages") ); } } diff --git a/src/Init/InitPluginCli.php b/src/Init/InitPluginCli.php index 3d6cfa2a9..7d498edc7 100644 --- a/src/Init/InitPluginCli.php +++ b/src/Init/InitPluginCli.php @@ -10,9 +10,10 @@ namespace EightshiftLibs\Init; +use EightshiftLibs\Cache\ManifestCacheCli; use EightshiftLibs\Cli\AbstractCli; use EightshiftLibs\Cli\ParentGroups\CliInit; -use EightshiftLibs\Config\ConfigCli; +use EightshiftLibs\Config\ConfigPluginCli; use EightshiftLibs\Main\MainCli; use ReflectionClass; @@ -31,7 +32,8 @@ class InitPluginCli extends AbstractCli 'type' => 'sc', 'label' => 'Setting service classes:', 'items' => [ - ConfigCli::class, + ManifestCacheCli::class, + ConfigPluginCli::class, MainCli::class, ], ], diff --git a/src/Init/InitThemeCli.php b/src/Init/InitThemeCli.php index d974c1bc4..bf3b77cc8 100644 --- a/src/Init/InitThemeCli.php +++ b/src/Init/InitThemeCli.php @@ -12,14 +12,14 @@ use EightshiftLibs\AdminMenus\AdminReusableBlocksMenuCli; use EightshiftLibs\AdminMenus\ReusableBlocksHeaderFooterCli; +use EightshiftLibs\Cache\ManifestCacheCli; use EightshiftLibs\Cli\AbstractCli; use EightshiftLibs\Cli\ParentGroups\CliInit; -use EightshiftLibs\Config\ConfigCli; +use EightshiftLibs\Config\ConfigThemeCli; use EightshiftLibs\Enqueue\Admin\EnqueueAdminCli; use EightshiftLibs\Enqueue\Blocks\EnqueueBlocksCli; use EightshiftLibs\Enqueue\Theme\EnqueueThemeCli; use EightshiftLibs\Main\MainCli; -use EightshiftLibs\Manifest\ManifestCli; use ReflectionClass; /** @@ -37,9 +37,9 @@ class InitThemeCli extends AbstractCli 'type' => 'sc', 'label' => 'Setting service classes:', 'items' => [ - ConfigCli::class, + ManifestCacheCli::class, + ConfigThemeCli::class, MainCli::class, - ManifestCli::class, EnqueueAdminCli::class, EnqueueBlocksCli::class, EnqueueThemeCli::class, diff --git a/src/Main/AbstractMain.php b/src/Main/AbstractMain.php index 964f6bf17..4f965fda9 100644 --- a/src/Main/AbstractMain.php +++ b/src/Main/AbstractMain.php @@ -187,7 +187,7 @@ private function getDiContainer(array $services): Container $builder = new ContainerBuilder(); - if (\defined('WP_ENVIRONMENT_TYPE') && (\WP_ENVIRONMENT_TYPE === 'production' || \WP_ENVIRONMENT_TYPE === 'staging')) { + if (\defined('WP_ENVIRONMENT_TYPE') && \WP_ENVIRONMENT_TYPE !== 'development') { $file = \explode('\\', $this->namespace); $builder->enableCompilation(__DIR__ . '/Cache', "{$file[0]}CompiledContainer"); diff --git a/src/Manifest/AbstractManifest.php b/src/Manifest/AbstractManifest.php deleted file mode 100644 index e024937bf..000000000 --- a/src/Manifest/AbstractManifest.php +++ /dev/null @@ -1,105 +0,0 @@ - - */ - protected $manifest = []; - - /** - * Set the manifest data with site url prefix. - * You should never call this method directly instead you should call $this->manifest. - * - * @throws InvalidManifest Throws error if manifest.json file is missing. - * - * @return void Sets the manifest variable. - */ - public function setAssetsManifestRaw(): void - { - if (\defined('WP_CLI')) { - return; - } - - $path = $this->getManifestFilePath(); - - if (!\file_exists($path)) { - throw InvalidManifest::missingManifestException($path); - } - - $data = \json_decode(\implode(' ', (array)\file($path)), true); - - if (empty($data)) { - return; - } - - $this->manifest = \array_map( - function ($manifestItem) { - return "{$this->getAssetsManifestOutputPrefix()}{$manifestItem}"; - }, - $data - ); - } - - /** - * Return full path for specific asset from manifest.json. - * - * @param string $key File name key you want to get from manifest. - * - * @throws InvalidManifest Throws error if manifest key is missing. - * Returns data from manifest and not global variable. - * - * @return string Full path to asset. - */ - public function getAssetsManifestItem(string $key): string - { - if (\defined('WP_CLI') || \getenv('ES_TEST')) { - return ''; - } - - $manifest = $this->manifest; - - if (!isset($manifest[$key])) { - throw InvalidManifest::missingManifestItemException($key); - } - - return $manifest[$key]; - } - - /** - * Manifest file path getter. - * - * @return string - */ - abstract protected function getManifestFilePath(): string; - - /** - * This method appends full site url to the relative manifest data item. - * - * @return string - */ - protected function getAssetsManifestOutputPrefix(): string - { - return \site_url(); - } -} diff --git a/src/Manifest/ManifestExample.php b/src/Manifest/ManifestExample.php deleted file mode 100644 index fb97e0523..000000000 --- a/src/Manifest/ManifestExample.php +++ /dev/null @@ -1,50 +0,0 @@ -getSetupFile(); - } catch (FileMissing $exception) { + } catch (InvalidPath $exception) { self::cliError($exception->getMessage()); } @@ -278,7 +278,7 @@ private function installPaidPlugins(array $setup) try { $this->installPaidPlugin($pluginToAdd, $version); - } catch (FileMissing $exception) { + } catch (InvalidPath $exception) { self::cliError($exception->getMessage()); } } @@ -335,7 +335,7 @@ private function installGHPlugin(string $name, string $version): void * * @return void * - * @throws FileMissing Exception in case the env.json file is missing. + * @throws InvalidPath Exception in case the env.json file is missing. */ private function installPaidPlugin(string $name, string $version): void { @@ -348,7 +348,7 @@ private function installPaidPlugin(string $name, string $version): void } if (!\file_exists($envFile)) { - throw FileMissing::missingFileOnPath($envFile); + throw InvalidPath::missingFileException($envFile); } $envData = \json_decode((string)\file_get_contents($envFile), true); @@ -370,7 +370,7 @@ private function installPaidPlugin(string $name, string $version): void * * @return array setup.json file in array form. * - * @throws FileMissing Throws exception in case the setup.json file is missing. + * @throws InvalidPath Throws exception in case the setup.json file is missing. */ private function getSetupFile(): array { @@ -382,7 +382,7 @@ private function getSetupFile(): array } if (!\file_exists($setupFile)) { - throw FileMissing::missingFileOnPath($setupFile); + throw InvalidPath::missingFileException($setupFile); } return (array)\json_decode((string)\file_get_contents($setupFile), true); @@ -513,7 +513,7 @@ private function updatePlugins(array $setup) } elseif (isset($paidSetupPlugins[$pluginName])) { try { $this->installPaidPlugin($pluginName, $setupPluginVersion); - } catch (FileMissing $exception) { + } catch (InvalidPath $exception) { self::cliError($exception->getMessage()); } } else {