diff --git a/.github/workflows/test-measure.yml b/.github/workflows/test-measure.yml index 7ade3296..5f6e4f81 100644 --- a/.github/workflows/test-measure.yml +++ b/.github/workflows/test-measure.yml @@ -88,7 +88,7 @@ jobs: [[ "${{ steps.determine-file-counts.outputs.css-count }}" -le 0 ]] && SKIPPED+=("lint-css") [[ "${{ steps.determine-file-counts.outputs.js-count }}" -le 0 ]] && SKIPPED+=("lint-js" "unit-tests-js" "build-prod") - [[ "${{ steps.determine-file-counts.outputs.php-count }}" -le 0 ]] && SKIPPED+=("lint-php" "unit-test-php") + [[ "${{ steps.determine-file-counts.outputs.php-count }}" -le 0 ]] && SKIPPED+=("lint-php") if [[ ${#SKIPPED[@]} -gt 0 ]]; then echo "The following jobs will be skipped as no relevant files were changed:" @@ -161,6 +161,7 @@ jobs: php-version: '8.2' coverage: none tools: cs2pr + github-token: ${{ secrets.WP_FRAMEWORK_REPO_TEMP_TOKEN }} - name: Get Composer Cache Directory id: composer-cache @@ -174,6 +175,9 @@ jobs: restore-keys: | ${{ runner.os }}-composer- + - name: Ensure git is installed + run: which git || (sudo apt-get update && sudo apt-get install -y git) + - name: Install Composer dependencies run: composer install --prefer-dist --optimize-autoloader --no-progress --no-interaction --no-scripts @@ -183,24 +187,6 @@ jobs: - name: Detect coding standard violations (PHPCS) run: vendor/bin/phpcs -q --report=checkstyle --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 | cs2pr --graceful-warnings - unit-test-php: - needs: pre-run - if: needs.pre-run.outputs.changed-php-count > 0 - name: "PHP Unit test" - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Setup Node with cache - uses: ./.github/actions/setup-node-with-cache - - - name: Start WP environment - run: npm run wp-env start - - - name: Run tests - run: npm run test:php - build-prod: needs: pre-run if: needs.pre-run.outputs.changed-js-count > 0 || needs.pre-run.outputs.changed-css-count > 0 diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 076d4819..846a6079 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -1,86 +1,133 @@ # Development Guide -## PSR-4 Namespace Convention +## Architecture overview -All namespaced PHP classes live under `inc/` using the `rtCamp\Theme\Elementary\` root namespace. -The file path must map to the namespace and class name. +The theme is split into two layers: -Examples: +- **`vendor/rtcamp/wp-framework/`** — The upstream framework, installed as a Composer dependency. Provides reusable scaffolding (`Singleton`, `Loader`, `Container`, `AssetLoaderTrait`, `TemplateLoaderTrait`) and abstract base classes (`AbstractSettingsPage`, `AbstractPostType`, etc.). **Do not modify.** Changes belong in the framework repository. +- **`inc/`** — All theme-specific code. Extends framework abstracts, registers theme services, and bootstraps the theme. -- `inc/Main.php` → `rtCamp\Theme\Elementary\Main` -- `inc/Core/Assets.php` → `rtCamp\Theme\Elementary\Core\Assets` -- `inc/BlockExtensions/MediaTextInteractive.php` → `rtCamp\Theme\Elementary\Modules\BlockExtensions\MediaTextInteractive` -- `inc/Framework/Traits/Singleton.php` → `rtCamp\Theme\Elementary\Framework\Traits\Singleton` +The `vendor/` boundary enforces the rule by convention: editing files there gets blown away on every `composer install`. -## Directory tree +## PSR-4 namespace convention -The `inc/` directory follows a PSR-4 structure and is split into meaningful areas: +Single PSR-4 root, declared in `composer.json`: -- `inc/Framework/` - - Upstream-owned framework code. - - Base traits, contracts, and utilities. - - Do not modify in downstream themes unless there is no other option. -- `inc/Main.php` - - Theme bootstrap entry point. - - Defines the primary theme class under `rtCamp\Theme\Elementary`. -- `inc/Core/` - - Project-specific core classes. - - Example: asset loading, theme setup, shared services. -- `inc/BlockExtensions/` - - Project-specific block extension classes. - - Example: block render filters, block-specific integrations. -- `inc/helpers/` - - Non-namespaced helper files. - - Loaded via Composer `files` autoload and not subject to PSR-4 class name rules. +```json +"autoload": { + "psr-4": { + "rtCamp\\Theme\\Elementary\\": "inc/" + } +} +``` + +Directory segments map 1:1 to namespace segments. Files are PascalCase. -## Two-layer model +| Namespace | File | +|------------------------------------------------------------------------|-------------------------------------------------------| +| `rtCamp\Theme\Elementary\Main` | `inc/Main.php` | +| `rtCamp\Theme\Elementary\Autoloader` | `inc/Autoloader.php` | +| `rtCamp\Theme\Elementary\Core\Assets` | `inc/Core/Assets.php` | +| `rtCamp\Theme\Elementary\Modules\BlockExtensions\MediaTextInteractive` | `inc/Modules/BlockExtensions/MediaTextInteractive.php`| +| `rtCamp\Theme\Elementary\Modules\Settings\ThemeOptions` | `inc/Modules/Settings/ThemeOptions.php` | +| `rtCamp\Theme\Elementary\Helpers\Util` | `inc/Helpers/Util.php` | -The repository is split into two layers: +## Directory layout -- `inc/Framework/` — upstream-owned framework code. Do not modify in downstream projects. -- `inc/` (outside `Framework/`) — project-specific implementation and customizations. +``` +inc/ +├── Autoloader.php # Wraps vendor/autoload.php with graceful failure +├── Main.php # Theme bootstrap — loads services +├── Helpers/ # Stateless static utility classes (final, private __construct) +│ └── Util.php # General-purpose helpers (add static methods as needed) +├── Core/ # Theme-wide infrastructure +│ └── Assets.php # Asset registration (uses AssetLoaderTrait) +└── Modules/ # Feature areas + ├── BlockExtensions/ # Block render filters and integrations + │ └── MediaTextInteractive.php + └── Settings/ # Admin settings pages (extend AbstractSettingsPage) + └── ThemeOptions.php +``` -### `inc/Framework/` +## Helpers -This is the base layer. It should contain only reusable traits, interfaces, and low-level utilities. -Treat it like vendored code. If you need to change behavior, extend the framework class in `inc/` instead. +`inc/Helpers/` is the home for stateless utility classes — `final`, `private __construct()`, static methods only. Today it holds one class, `Util`, kept as a placeholder for theme-wide helpers that don't earn their own dedicated class. Add siblings (e.g. `Str`, `Cache`, `Url`) as cross-cutting helpers accumulate, rather than letting `Util` grow into a grab-bag. -### `inc/` +## Picking a base -This is the application layer for this theme. Add new feature classes, hooks, and theme-specific behavior here. +| Feature | Extends / implements | +|------------------------------------------|-------------------------------------| +| Settings page | `AbstractSettingsPage` | +| Admin (non-settings) page | `AbstractAdminPage` | +| Dynamic block (server-side render) | `AbstractBlock` | +| REST controller | `AbstractRESTController` | +| Shortcode | `AbstractShortcode` | +| Anything else that just wires hooks | `Registrable` interface | +| Same, but registration is conditional | `ConditionallyRegistrable` interface| ## Adding a new class -1. Create a new PHP file under `inc/` using PascalCase file names. -2. Use the matching namespace path. -3. Define the class name to match the filename exactly. -4. If the class belongs to a feature group, create a subdirectory and namespace that group. +1. Pick the right abstract or interface from the table above. +2. Drop the file in the matching `inc/Modules//` directory (or `inc/Core/` if it's theme-wide infrastructure). +3. Register it in `Main::__construct()`'s `$this->load( [ … ] )` call. +4. Run `composer dump-autoload`. Example: -`inc/Example/Feature.php` - ```php -namespace rtCamp\Theme\Elementary\Example; +// inc/Modules/Example/Feature.php +namespace rtCamp\Theme\Elementary\Modules\Example; + +use rtCamp\WPFramework\Contracts\Interfaces\Registrable; -class Feature { - // ... +final class Feature implements Registrable { + public function register_hooks(): void { + add_action( 'init', [ $this, 'do_something' ] ); + } + + public function do_something(): void { + // ... + } } ``` -## Existing non-namespaced file +Then in `Main::__construct()`: + +```php +$this->load( [ + Assets::class, + MediaTextInteractive::class, + ThemeOptions::class, + \rtCamp\Theme\Elementary\Modules\Example\Feature::class, +] ); +``` + +## Conditional registration -`inc/helpers/custom-functions.php` is intentionally not namespaced and remains loaded through Composer `files` autoload. +A class can opt out of registration at runtime by implementing `ConditionallyRegistrable` instead of `Registrable`: -## Running autoload generation +```php +final class DevToolbarExtension implements ConditionallyRegistrable { + public function can_register(): bool { + return defined( 'WP_DEBUG' ) && WP_DEBUG; + } + + public function register_hooks(): void { + // Wire dev-only hooks here. + } +} +``` + +The `Loader` calls `can_register()` first and skips `register_hooks()` when it returns false. -After moving or adding namespaced classes, regenerate Composer autoload files: +## Running Composer ```bash +# First-time setup +composer install + +# After adding, renaming, or moving a class composer dump-autoload ``` -## Notes - -- Do not use `classmap` autoloading for namespaced classes. -- Keep the `rtCamp\Theme\Elementary\` PSR-4 root aligned with `inc/`. +If `vendor/autoload.php` is missing at runtime, the theme shows an admin notice instead of fataling — see `inc/Autoloader.php` and `AutoloaderTrait` in the framework. diff --git a/README.md b/README.md index 1311e193..5e761bb2 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,17 @@ A starter theme that facilitates a quick head start for developing new [block-based themes](https://developer.wordpress.org/block-editor/how-to-guides/themes/block-theme-overview/) along with a bunch of developer-friendly features. -- [Understand the Folder Structure](https://github.com/rtCamp/theme-elementary#understand-the-folder-structure-open_file_folder) -- [Get Started](https://github.com/rtCamp/theme-elementary#get-started-rocket) -- [Development](https://github.com/rtCamp/theme-elementary#development-computer) +Reusable scaffolding (singleton, autoloader, asset loader, template loader, and abstract base classes) ships separately as the `rtcamp/wp-framework` Composer package and is loaded from `vendor/`. + +> **Working on this theme?** See [DEVELOPMENT.md](DEVELOPMENT.md) for the architecture overview, the module pattern, and how to add new classes. + +- [Understand the Folder Structure](#understand-the-folder-structure-open_file_folder) +- [Get Started](#get-started-rocket) +- [Development](#development-computer) ## Understand the Folder Structure :open_file_folder: ``` - . +. ├── src (Frontend source) │ ├── css │ │ ├── frontend/ @@ -30,11 +34,14 @@ A starter theme that facilitates a quick head start for developing new [block-ba │ └── build (Compiled output) ├── bin (Holds scripts) ├── functions.php (PHP entry point) -├── inc -│ ├── Core/ (Project-specific core classes) -│ ├── BlockExtensions/ (Block extension classes) -│ ├── Framework/ (Upstream framework code) -│ └── helpers/ (Non-namespaced helpers) +├── inc (All project-specific PHP — PSR-4 root) +│ ├── Autoloader.php (Wraps vendor/autoload.php with graceful failure) +│ ├── Main.php (Theme bootstrap — loads services) +│ ├── Helpers/ (Stateless static utility classes) +│ ├── Core/ (Theme-wide infrastructure — assets, etc.) +│ └── Modules/ (Feature areas) +│ ├── BlockExtensions/ (Block render filters and integrations) +│ └── Settings/ (Admin settings pages — extend AbstractSettingsPage) ├── parts (Block Template Parts) ├── patterns (Block Patterns) ├── style.css @@ -42,8 +49,10 @@ A starter theme that facilitates a quick head start for developing new [block-ba ├── tests (Holds JS & PHP tests) │ ├── js/ │ └── php/ +├── vendor +│ └── rtcamp/wp-framework/ (Framework — do not modify; Composer-managed) +├── DEVELOPMENT.md └── theme.json - ``` ## Get Started :rocket: diff --git a/composer.json b/composer.json index e3417f82..c724b4d5 100644 --- a/composer.json +++ b/composer.json @@ -4,8 +4,16 @@ "type": "wordpress-theme", "homepage": "https://rtcamp.com/", "license": "GPL-2.0-or-later", + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/rtCamp/wp-framework.git", + "no-api": true + } + ], "require": { - "php": ">=8.2" + "php": ">=8.2", + "rtcamp/wp-framework": "dev-framework" }, "require-dev": { "wp-coding-standards/wpcs": "^2.3", @@ -34,10 +42,7 @@ "autoload": { "psr-4": { "rtCamp\\Theme\\Elementary\\": "inc/" - }, - "files": [ - "inc/helpers/custom-functions.php" - ] + } }, "autoload-dev": { "psr-4": { diff --git a/composer.lock b/composer.lock index 0c9d4e10..776e4b5e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,73 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9934fc21777ac929aeff9af41ee57eeb", - "packages": [], + "content-hash": "5c9d115741ba03076111d8506b4f0895", + "packages": [ + { + "name": "rtcamp/wp-framework", + "version": "dev-framework", + "source": { + "type": "git", + "url": "https://github.com/rtCamp/wp-framework.git", + "reference": "de18dff78d39b4a4cc5a6aaef39251db19751c32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rtCamp/wp-framework/zipball/de18dff78d39b4a4cc5a6aaef39251db19751c32", + "reference": "de18dff78d39b4a4cc5a6aaef39251db19751c32", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "automattic/vipwpcs": "^3.0", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "phpcompatibility/phpcompatibility-wp": "^2.1", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^11.0", + "squizlabs/php_codesniffer": "^3.10", + "szepeviktor/phpstan-wordpress": "^2.0", + "wp-coding-standards/wpcs": "^3.1", + "yoast/phpunit-polyfills": "^4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "rtCamp\\WPFramework\\": "inc/" + } + }, + "autoload-dev": { + "psr-4": { + "rtCamp\\WPFramework\\Tests\\": "tests/" + } + }, + "scripts": { + "lint": [ + "phpcs" + ], + "lint:fix": [ + "phpcbf" + ], + "analyse": [ + "phpstan analyse --memory-limit=1G" + ], + "test": [ + "phpunit" + ], + "check": [ + "@lint", + "@analyse", + "@test" + ] + }, + "license": [ + "GPL-2.0-or-later" + ], + "description": "A reusable WordPress framework providing composable traits, abstract classes, and interfaces for plugins and themes.", + "time": "2026-05-21T23:13:32+00:00" + } + ], "packages-dev": [ { "name": "automattic/vipwpcs", @@ -367,16 +432,16 @@ }, { "name": "gettext/languages", - "version": "2.12.1", + "version": "2.12.2", "source": { "type": "git", "url": "https://github.com/php-gettext/Languages.git", - "reference": "0b0b0851c55168e1dfb14305735c64019732b5f1" + "reference": "079d6f4842cbcbf5673a70d8e93169a684e7aadd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-gettext/Languages/zipball/0b0b0851c55168e1dfb14305735c64019732b5f1", - "reference": "0b0b0851c55168e1dfb14305735c64019732b5f1", + "url": "https://api.github.com/repos/php-gettext/Languages/zipball/079d6f4842cbcbf5673a70d8e93169a684e7aadd", + "reference": "079d6f4842cbcbf5673a70d8e93169a684e7aadd", "shasum": "" }, "require": { @@ -426,7 +491,7 @@ ], "support": { "issues": "https://github.com/php-gettext/Languages/issues", - "source": "https://github.com/php-gettext/Languages/tree/2.12.1" + "source": "https://github.com/php-gettext/Languages/tree/2.12.2" }, "funding": [ { @@ -438,20 +503,20 @@ "type": "github" } ], - "time": "2025-03-19T11:14:02+00:00" + "time": "2026-02-23T14:05:50+00:00" }, { "name": "mck89/peast", - "version": "v1.17.4", + "version": "v1.17.6", "source": { "type": "git", "url": "https://github.com/mck89/peast.git", - "reference": "c6a63f32410d2e4ee2cd20fe94b35af147fb852d" + "reference": "b8b4184b1e6912669f9af155caef9050509d9f18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mck89/peast/zipball/c6a63f32410d2e4ee2cd20fe94b35af147fb852d", - "reference": "c6a63f32410d2e4ee2cd20fe94b35af147fb852d", + "url": "https://api.github.com/repos/mck89/peast/zipball/b8b4184b1e6912669f9af155caef9050509d9f18", + "reference": "b8b4184b1e6912669f9af155caef9050509d9f18", "shasum": "" }, "require": { @@ -464,7 +529,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.17.4-dev" + "dev-master": "1.17.6-dev" } }, "autoload": { @@ -485,9 +550,62 @@ "description": "Peast is PHP library that generates AST for JavaScript code", "support": { "issues": "https://github.com/mck89/peast/issues", - "source": "https://github.com/mck89/peast/tree/v1.17.4" + "source": "https://github.com/mck89/peast/tree/v1.17.6" + }, + "time": "2026-04-24T08:04:05+00:00" + }, + { + "name": "mustache/mustache", + "version": "v3.2.0", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/mustache.php.git", + "reference": "bd4fb2e45ac2df0570c0f4da6898054a950d1ed0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/bd4fb2e45ac2df0570c0f4da6898054a950d1ed0", + "reference": "bd4fb2e45ac2df0570c0f4da6898054a950d1ed0", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.19.3", + "yoast/phpunit-polyfills": "^2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Mustache\\": "src/" + }, + "classmap": [ + "src/compat.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info", + "homepage": "http://justinhileman.com" + } + ], + "description": "A Mustache implementation in PHP.", + "homepage": "https://github.com/bobthecow/mustache.php", + "keywords": [ + "mustache", + "templating" + ], + "support": { + "issues": "https://github.com/bobthecow/mustache.php/issues", + "source": "https://github.com/bobthecow/mustache.php/tree/v3.2.0" }, - "time": "2025-10-10T12:53:17+00:00" + "time": "2026-05-10T04:13:08+00:00" }, { "name": "myclabs/deep-copy", @@ -727,16 +845,16 @@ }, { "name": "php-stubs/wordpress-stubs", - "version": "v6.9.1", + "version": "v6.9.4", "source": { "type": "git", "url": "https://github.com/php-stubs/wordpress-stubs.git", - "reference": "f12220f303e0d7c0844c0e5e957b0c3cee48d2f7" + "reference": "90a9412826b9944f93b10bf41d795b5fe68abcd5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/f12220f303e0d7c0844c0e5e957b0c3cee48d2f7", - "reference": "f12220f303e0d7c0844c0e5e957b0c3cee48d2f7", + "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/90a9412826b9944f93b10bf41d795b5fe68abcd5", + "reference": "90a9412826b9944f93b10bf41d795b5fe68abcd5", "shasum": "" }, "conflict": { @@ -746,7 +864,7 @@ "dealerdirect/phpcodesniffer-composer-installer": "^1.0", "nikic/php-parser": "^5.5", "php": "^7.4 || ^8.0", - "php-stubs/generator": "^0.8.3", + "php-stubs/generator": "^0.8.6", "phpdocumentor/reflection-docblock": "^6.0", "phpstan/phpstan": "^2.1", "phpunit/phpunit": "^9.5", @@ -773,9 +891,9 @@ ], "support": { "issues": "https://github.com/php-stubs/wordpress-stubs/issues", - "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.9.1" + "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.9.4" }, - "time": "2026-02-03T19:29:21+00:00" + "time": "2026-05-01T20:36:01+00:00" }, { "name": "php-stubs/wp-cli-stubs", @@ -885,16 +1003,16 @@ }, { "name": "phpcompatibility/phpcompatibility-paragonie", - "version": "1.3.3", + "version": "1.3.4", "source": { "type": "git", "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git", - "reference": "293975b465e0e709b571cbf0c957c6c0a7b9a2ac" + "reference": "244d7b04fc4bc2117c15f5abe23eb933b5f02bbf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/293975b465e0e709b571cbf0c957c6c0a7b9a2ac", - "reference": "293975b465e0e709b571cbf0c957c6c0a7b9a2ac", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/244d7b04fc4bc2117c15f5abe23eb933b5f02bbf", + "reference": "244d7b04fc4bc2117c15f5abe23eb933b5f02bbf", "shasum": "" }, "require": { @@ -951,27 +1069,32 @@ { "url": "https://opencollective.com/php_codesniffer", "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcompatibility", + "type": "thanks_dev" } ], - "time": "2024-04-24T21:30:46+00:00" + "time": "2025-09-19T17:43:28+00:00" }, { "name": "phpcompatibility/phpcompatibility-wp", - "version": "2.1.5", + "version": "2.1.8", "source": { "type": "git", "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git", - "reference": "01c1ff2704a58e46f0cb1ca9d06aee07b3589082" + "reference": "7c8d18b4d90dac9e86b0869a608fa09158e168fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/01c1ff2704a58e46f0cb1ca9d06aee07b3589082", - "reference": "01c1ff2704a58e46f0cb1ca9d06aee07b3589082", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/7c8d18b4d90dac9e86b0869a608fa09158e168fa", + "reference": "7c8d18b4d90dac9e86b0869a608fa09158e168fa", "shasum": "" }, "require": { "phpcompatibility/php-compatibility": "^9.0", - "phpcompatibility/phpcompatibility-paragonie": "^1.0" + "phpcompatibility/phpcompatibility-paragonie": "^1.0", + "squizlabs/php_codesniffer": "^3.3" }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "^1.0" @@ -1021,9 +1144,13 @@ { "url": "https://opencollective.com/php_codesniffer", "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcompatibility", + "type": "thanks_dev" } ], - "time": "2024-04-24T21:37:59+00:00" + "time": "2025-10-18T00:05:59+00:00" }, { "name": "phpstan/extension-installer", @@ -1075,11 +1202,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.51", + "version": "2.1.55", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc3b523c45e714c70de2ac5113b958223b55dc59", - "reference": "dc3b523c45e714c70de2ac5113b958223b55dc59", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9eaac3826ed5e9b8427350a43cac825eeca3f566", + "reference": "9eaac3826ed5e9b8427350a43cac825eeca3f566", "shasum": "" }, "require": { @@ -1124,7 +1251,7 @@ "type": "github" } ], - "time": "2026-04-21T18:22:01+00:00" + "time": "2026-05-18T11:57:34+00:00" }, { "name": "phpstan/phpstan-phpunit", @@ -2625,28 +2752,27 @@ }, { "name": "sirbrillig/phpcs-variable-analysis", - "version": "v2.11.22", + "version": "v2.13.0", "source": { "type": "git", "url": "https://github.com/sirbrillig/phpcs-variable-analysis.git", - "reference": "ffb6f16c6033ec61ed84446b479a31d6529f0eb7" + "reference": "a15e970b8a0bf64cfa5e86d941f5e6b08855f369" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/ffb6f16c6033ec61ed84446b479a31d6529f0eb7", - "reference": "ffb6f16c6033ec61ed84446b479a31d6529f0eb7", + "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/a15e970b8a0bf64cfa5e86d941f5e6b08855f369", + "reference": "a15e970b8a0bf64cfa5e86d941f5e6b08855f369", "shasum": "" }, "require": { "php": ">=5.4.0", - "squizlabs/php_codesniffer": "^3.5.6" + "squizlabs/php_codesniffer": "^3.5.7 || ^4.0.0" }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || ^1.0", - "phpcsstandards/phpcsdevcs": "^1.1", - "phpstan/phpstan": "^1.7", + "phpstan/phpstan": "^1.7 || ^2.0", "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.5 || ^7.0 || ^8.0 || ^9.0 || ^10.5.32 || ^11.3.3", - "vimeo/psalm": "^0.2 || ^0.3 || ^1.1 || ^4.24 || ^5.0" + "vimeo/psalm": "^0.2 || ^0.3 || ^1.1 || ^4.24 || ^5.0 || ^6.0 || ^7.0" }, "type": "phpcodesniffer-standard", "autoload": { @@ -2678,7 +2804,7 @@ "source": "https://github.com/sirbrillig/phpcs-variable-analysis", "wiki": "https://github.com/sirbrillig/phpcs-variable-analysis/wiki" }, - "time": "2025-01-06T17:54:24+00:00" + "time": "2025-09-30T22:22:48+00:00" }, { "name": "squizlabs/php_codesniffer", @@ -2759,74 +2885,6 @@ ], "time": "2025-11-04T16:30:35+00:00" }, - { - "name": "symfony/finder", - "version": "v7.4.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "fffe05569336549b20a1be64250b40516d6e8d06" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/fffe05569336549b20a1be64250b40516d6e8d06", - "reference": "fffe05569336549b20a1be64250b40516d6e8d06", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "symfony/filesystem": "^6.4|^7.0|^8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Finds files and directories via an intuitive fluent interface", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/finder/tree/v7.4.3" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-12-23T14:50:43+00:00" - }, { "name": "szepeviktor/phpstan-wordpress", "version": "v2.0.3", @@ -2942,23 +3000,23 @@ }, { "name": "wp-cli/i18n-command", - "version": "v2.6.6", + "version": "v2.7.3", "source": { "type": "git", "url": "https://github.com/wp-cli/i18n-command.git", - "reference": "94f72ddc4be8919f2cea181ba39cd140dd480d64" + "reference": "a42366245fef8b11a5d895db3f38d3c606d21d06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wp-cli/i18n-command/zipball/94f72ddc4be8919f2cea181ba39cd140dd480d64", - "reference": "94f72ddc4be8919f2cea181ba39cd140dd480d64", + "url": "https://api.github.com/repos/wp-cli/i18n-command/zipball/a42366245fef8b11a5d895db3f38d3c606d21d06", + "reference": "a42366245fef8b11a5d895db3f38d3c606d21d06", "shasum": "" }, "require": { "eftec/bladeone": "3.52", "gettext/gettext": "^4.8", "mck89/peast": "^1.13.11", - "wp-cli/wp-cli": "^2.12" + "wp-cli/wp-cli": "^2.13" }, "require-dev": { "wp-cli/scaffold-command": "^1.2 || ^2", @@ -2973,6 +3031,7 @@ "bundled": true, "commands": [ "i18n", + "i18n audit", "i18n make-pot", "i18n make-json", "i18n make-mo", @@ -3005,74 +3064,22 @@ "homepage": "https://github.com/wp-cli/i18n-command", "support": { "issues": "https://github.com/wp-cli/i18n-command/issues", - "source": "https://github.com/wp-cli/i18n-command/tree/v2.6.6" + "source": "https://github.com/wp-cli/i18n-command/tree/v2.7.3" }, - "time": "2025-11-21T04:23:34+00:00" - }, - { - "name": "wp-cli/mustache", - "version": "v2.14.99", - "source": { - "type": "git", - "url": "https://github.com/wp-cli/mustache.php.git", - "reference": "ca23b97ac35fbe01c160549eb634396183d04a59" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/mustache.php/zipball/ca23b97ac35fbe01c160549eb634396183d04a59", - "reference": "ca23b97ac35fbe01c160549eb634396183d04a59", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "replace": { - "mustache/mustache": "^2.14.2" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "~2.19.3", - "yoast/phpunit-polyfills": "^2.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "Mustache": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Justin Hileman", - "email": "justin@justinhileman.info", - "homepage": "http://justinhileman.com" - } - ], - "description": "A Mustache implementation in PHP.", - "homepage": "https://github.com/bobthecow/mustache.php", - "keywords": [ - "mustache", - "templating" - ], - "support": { - "source": "https://github.com/wp-cli/mustache.php/tree/v2.14.99" - }, - "time": "2025-05-06T16:15:37+00:00" + "time": "2026-04-28T13:55:33+00:00" }, { "name": "wp-cli/mustangostang-spyc", - "version": "0.6.3", + "version": "0.6.6", "source": { "type": "git", "url": "https://github.com/wp-cli/spyc.git", - "reference": "6aa0b4da69ce9e9a2c8402dab8d43cf32c581cc7" + "reference": "30f25baaaba939caaff1f4b8c7ed998632f59fe2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wp-cli/spyc/zipball/6aa0b4da69ce9e9a2c8402dab8d43cf32c581cc7", - "reference": "6aa0b4da69ce9e9a2c8402dab8d43cf32c581cc7", + "url": "https://api.github.com/repos/wp-cli/spyc/zipball/30f25baaaba939caaff1f4b8c7ed998632f59fe2", + "reference": "30f25baaaba939caaff1f4b8c7ed998632f59fe2", "shasum": "" }, "require": { @@ -3108,22 +3115,22 @@ "description": "A simple YAML loader/dumper class for PHP (WP-CLI fork)", "homepage": "https://github.com/mustangostang/spyc/", "support": { - "source": "https://github.com/wp-cli/spyc/tree/autoload" + "source": "https://github.com/wp-cli/spyc/tree/0.6.6" }, - "time": "2017-04-25T11:26:20+00:00" + "time": "2026-03-12T12:30:41+00:00" }, { "name": "wp-cli/php-cli-tools", - "version": "v0.12.6", + "version": "v0.12.9", "source": { "type": "git", "url": "https://github.com/wp-cli/php-cli-tools.git", - "reference": "f12b650d3738e471baed6dd47982d53c5c0ab1c3" + "reference": "c3d25138ce46a66647ec0dc9b17bf300338494aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wp-cli/php-cli-tools/zipball/f12b650d3738e471baed6dd47982d53c5c0ab1c3", - "reference": "f12b650d3738e471baed6dd47982d53c5c0ab1c3", + "url": "https://api.github.com/repos/wp-cli/php-cli-tools/zipball/c3d25138ce46a66647ec0dc9b17bf300338494aa", + "reference": "c3d25138ce46a66647ec0dc9b17bf300338494aa", "shasum": "" }, "require": { @@ -3136,7 +3143,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "0.12.x-dev" + "dev-main": "0.12.x-dev" } }, "autoload": { @@ -3171,51 +3178,66 @@ ], "support": { "issues": "https://github.com/wp-cli/php-cli-tools/issues", - "source": "https://github.com/wp-cli/php-cli-tools/tree/v0.12.6" + "source": "https://github.com/wp-cli/php-cli-tools/tree/v0.12.9" }, - "time": "2025-09-11T12:43:04+00:00" + "time": "2026-03-29T11:12:54+00:00" }, { "name": "wp-cli/wp-cli", - "version": "v2.12.0", + "version": "dev-main", "source": { "type": "git", "url": "https://github.com/wp-cli/wp-cli.git", - "reference": "03d30d4138d12b4bffd8b507b82e56e129e0523f" + "reference": "4177cb58ca649407e390a264aa19577a4125fdfe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wp-cli/wp-cli/zipball/03d30d4138d12b4bffd8b507b82e56e129e0523f", - "reference": "03d30d4138d12b4bffd8b507b82e56e129e0523f", + "url": "https://api.github.com/repos/wp-cli/wp-cli/zipball/4177cb58ca649407e390a264aa19577a4125fdfe", + "reference": "4177cb58ca649407e390a264aa19577a4125fdfe", "shasum": "" }, "require": { - "ext-curl": "*", - "php": "^5.6 || ^7.0 || ^8.0", - "symfony/finder": ">2.7", - "wp-cli/mustache": "^2.14.99", + "mustache/mustache": "^3.0.0", + "php": ">=7.2.24 || ^8.0", "wp-cli/mustangostang-spyc": "^0.6.3", - "wp-cli/php-cli-tools": "~0.12.4" + "wp-cli/php-cli-tools": "~0.12.7" }, "require-dev": { - "wp-cli/db-command": "^1.3 || ^2", - "wp-cli/entity-command": "^1.2 || ^2", - "wp-cli/extension-command": "^1.1 || ^2", - "wp-cli/package-command": "^1 || ^2", - "wp-cli/wp-cli-tests": "^4.3.10" + "justinrainbow/json-schema": "^6.3", + "roave/security-advisories": "dev-latest", + "wp-cli/db-command": "^2", + "wp-cli/entity-command": "^2", + "wp-cli/extension-command": "^2", + "wp-cli/package-command": "^2", + "wp-cli/wp-cli-tests": "^5" }, "suggest": { + "ext-curl": "For better performance when making HTTP requests", "ext-readline": "Include for a better --prompt implementation", "ext-zip": "Needed to support extraction of ZIP archives when doing downloads or updates" }, + "default-branch": true, "bin": [ "bin/wp", "bin/wp.bat" ], "type": "library", "extra": { + "commands": [ + "cli", + "cli alias", + "cli cache", + "cli check-update", + "cli cmd-dump", + "cli completions", + "cli has-command", + "cli info", + "cli param-dump", + "cli update", + "cli version" + ], "branch-alias": { - "dev-main": "2.12.x-dev" + "dev-main": "2.13.x-dev" } }, "autoload": { @@ -3242,7 +3264,7 @@ "issues": "https://github.com/wp-cli/wp-cli/issues", "source": "https://github.com/wp-cli/wp-cli" }, - "time": "2025-05-07T01:16:12+00:00" + "time": "2026-05-21T20:19:24+00:00" }, { "name": "wp-coding-standards/wpcs", @@ -3297,7 +3319,7 @@ }, { "name": "wp-phpunit/wp-phpunit", - "version": "6.9.1", + "version": "6.9.4", "source": { "type": "git", "url": "https://github.com/wp-phpunit/wp-phpunit.git", @@ -3345,16 +3367,16 @@ }, { "name": "yoast/phpunit-polyfills", - "version": "3.1.1", + "version": "3.1.2", "source": { "type": "git", "url": "https://github.com/Yoast/PHPUnit-Polyfills.git", - "reference": "e6381c62c4df51677b657fbac79b79dfce7acdab" + "reference": "9cf2ccd990eadfc4a1e390592d4731e590b2c618" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/e6381c62c4df51677b657fbac79b79dfce7acdab", - "reference": "e6381c62c4df51677b657fbac79b79dfce7acdab", + "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/9cf2ccd990eadfc4a1e390592d4731e590b2c618", + "reference": "9cf2ccd990eadfc4a1e390592d4731e590b2c618", "shasum": "" }, "require": { @@ -3369,7 +3391,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.x-dev" + "dev-main": "4.x-dev" } }, "autoload": { @@ -3404,17 +3426,19 @@ "security": "https://github.com/Yoast/PHPUnit-Polyfills/security/policy", "source": "https://github.com/Yoast/PHPUnit-Polyfills" }, - "time": "2025-01-12T08:41:37+00:00" + "time": "2025-02-09T18:36:24+00:00" } ], "aliases": [], "minimum-stability": "dev", - "stability-flags": {}, + "stability-flags": { + "rtcamp/wp-framework": 20 + }, "prefer-stable": true, "prefer-lowest": false, "platform": { "php": ">=8.2" }, "platform-dev": {}, - "plugin-api-version": "2.9.0" + "plugin-api-version": "2.6.0" } diff --git a/inc/Autoloader.php b/inc/Autoloader.php index 3356d0ad..dd86bdf3 100644 --- a/inc/Autoloader.php +++ b/inc/Autoloader.php @@ -1,6 +1,6 @@ setup_hooks(); + public function __construct() { + $this->plugin_dir = ELEMENTARY_THEME_TEMP_DIR . '/'; + $this->plugin_url = trailingslashit( get_template_directory_uri() ) . '/'; + $this->assets_dir = 'assets/build'; } /** - * Setup hooks. + * Register hooks. * * @since 1.0.0 */ - public function setup_hooks(): void { + public function register_hooks(): void { add_action( 'wp_enqueue_scripts', [ $this, 'register_assets' ] ); add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_assets' ] ); add_filter( 'render_block', [ $this, 'enqueue_block_specific_assets' ], 10, 2 ); @@ -49,9 +49,9 @@ public function setup_hooks(): void { * @action wp_enqueue_scripts */ public function register_assets(): void { - $this->register_script( 'core-navigation', 'js/frontend/core-navigation.js' ); - $this->register_style( 'core-navigation', 'css/frontend/core-navigation.css' ); - $this->register_style( 'elementary-theme-styles', 'css/frontend/styles.css' ); + $this->register_script( 'core-navigation', 'js/frontend/core-navigation' ); + $this->register_style( 'core-navigation', 'css/frontend/core-navigation' ); + $this->register_style( 'elementary-theme-styles', 'css/frontend/styles' ); } /** diff --git a/inc/Framework/README.md b/inc/Framework/README.md deleted file mode 100644 index d3028fd4..00000000 --- a/inc/Framework/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# Framework Layer - -`inc/Framework/` is the upstream-owned base layer for this theme. -It contains shared contracts, traits, and utilities that downstream projects may extend, but should not edit directly. - -## What belongs here - -- Base traits -- Base interfaces or contracts -- Core utilities that define behaviour for the theme skeleton - -## What does not belong here - -- Project-specific customizations -- Theme-specific feature code -- Any code that would need direct modification for a downstream project - -## Why this exists - -This directory separates the skeleton internals from the theme implementation. -Without a clear boundary, downstream developers can accidentally modify base code and make it difficult to merge upstream improvements. - -## How to use it - -- Treat `inc/Framework/` as a vendored layer. -- Do not edit files inside `inc/Framework/` unless there is absolutely no other option. -- To override behaviour, extend the upstream class or override a method in a project-specific class under `inc/`. - -## Example - -If you need a custom singleton implementation or a different asset loader, do not modify `inc/Framework/Traits/Singleton.php`. -Instead, extend the base class or create a new class under `inc/`. diff --git a/inc/Framework/Traits/AssetLoaderTrait.php b/inc/Framework/Traits/AssetLoaderTrait.php deleted file mode 100644 index 3dd501c1..00000000 --- a/inc/Framework/Traits/AssetLoaderTrait.php +++ /dev/null @@ -1,123 +0,0 @@ - $deps Optional. An array of registered script handles this script depends on. Default empty array. - * @param string|bool|null $ver Optional. String specifying script version number, if not set, filetime will be used as version number. - * @param bool $in_footer Optional. Whether to enqueue the script before
instead of in the
.
- * Default 'false'.
- *
- * @return bool Whether the script has been registered. True on success, false on failure.
- *
- * @since 1.0.0
- */
- private function register_script( string $handle, string $file, array $deps = [], string|bool|null $ver = false, bool $in_footer = true ): bool {
- $file_path = sprintf( '%s/%s', ELEMENTARY_THEME_BUILD_DIR, $file );
-
- if ( ! \file_exists( $file_path ) ) {
- return false;
- }
-
- $src = sprintf( ELEMENTARY_THEME_BUILD_URI . '/%s', $file );
- $asset_meta = $this->get_asset_meta( $file, $deps );
-
- return wp_register_script( $handle, $src, $asset_meta['dependencies'], $asset_meta['version'], $in_footer );
- }
-
- /**
- * Register a CSS stylesheet.
- *
- * @param string $handle Name of the stylesheet. Should be unique.
- * @param string $file Style file, path of the script relative to the assets/build/ directory.
- * @param array ' . esc_html( $error_message ) . ' ' . esc_html__( 'Theme-wide options exposed to the editor and front-end.', 'elementary-theme' ) . ' %sget_page_title() ); ?>
+
+