Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d86ca2e
Allow an asset to be defined with 'css' / 'js' / 'blocks' prefix to e…
Sep 18, 2025
aff226d
Updated docs.
Sep 18, 2025
c92e9ee
Fix tests; don't use WP filesystem.
Sep 18, 2025
db5bfa7
Move HeadOverrides to framework.
Sep 18, 2025
7119422
Move Emoji to framework.
Sep 18, 2025
f5eacf5
Fix failing test.
Sep 18, 2025
04f919e
Updated documentation.
Sep 18, 2025
f61e584
Added BlockRegistrar, tests and documentation.
jamesmorrison Sep 22, 2025
b48e546
Fixed static analysis.
Sep 25, 2025
b06c5f4
Revert "Revert "Feature/typing""
darylldoyle Nov 13, 2025
deec478
Move class-loader cache to build-time and add loader debug page
darylldoyle Jun 29, 2026
0235e11
Polish the loader debug page UI
darylldoyle Jun 29, 2026
0696419
test: cover the build command and debug diagnostics; add loader timing
rleeson Jul 7, 2026
90a3956
test: address code-review feedback on timing and diagnostics
rleeson Jul 7, 2026
3fcfa84
harden cache resilience, fix timing correctness/tests, document mono-…
rleeson Jul 10, 2026
0fac09c
show absolute UTC build time in the loader cache detail
rleeson Jul 10, 2026
1c5ed6f
grow the loader card to fit an expanded class list
rleeson Jul 10, 2026
14befa7
Remove unused namespace reference
rleeson Jul 10, 2026
bf12204
Merge pull request #36 from 10up/feature/loader-tests-and-timing
darylldoyle Jul 30, 2026
0d45ee7
Surface corrupt-cache fallback on the debug page
darylldoyle Jul 30, 2026
c64052c
Merge branch 'develop' into feature/17-register-all-blocks-in-project
darylldoyle Jul 30, 2026
b869a55
Merge branch 'develop' into feature/13-set-file-type-in-get-asset-info
darylldoyle Jul 30, 2026
ea58e02
Merge branch 'develop' into revert-28-revert-21-feature/typing
darylldoyle Jul 30, 2026
2caf71e
Merge remote-tracking branch 'origin/feature/13-set-file-type-in-get-…
darylldoyle Jul 30, 2026
98b807d
Merge remote-tracking branch 'origin/feature/15-move-header-overrides…
darylldoyle Jul 30, 2026
9c9bb91
Merge remote-tracking branch 'origin/fix/issue-30-autoloader-cache-re…
darylldoyle Jul 30, 2026
bd04849
Merge branch 'feature/2.0.0' into revert-28-revert-21-feature/typing
darylldoyle Jul 30, 2026
9651e19
Fix composer.json
darylldoyle Jul 30, 2026
22e3eba
refactor!: declare native types across src/ for full type coverage
darylldoyle Jul 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- name: Setup PHP with composer v2
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.PHP_EXTENSIONS }}
php-version: ${{ env.PHP_VERSION }}
tools: composer:v2

- name: Validate composer.json and composer.lock
Expand All @@ -45,7 +45,7 @@ jobs:
- name: Setup PHP with composer v2
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.PHP_EXTENSIONS }}
php-version: ${{ env.PHP_VERSION }}
tools: composer:v2

- name: Install dependencies
Expand All @@ -64,7 +64,7 @@ jobs:
- name: Setup PHP with composer v2
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.PHP_EXTENSIONS }}
php-version: ${{ env.PHP_VERSION }}
tools: composer:v2

- name: Install dependencies
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
vendor/
coverage/
.phpunit.result.cache

# Generated class-loader cache (a build artefact, not source)
class-loader-cache/
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file, per [the Keep a Changelog standard](http://keepachangelog.com/) and will adhere to [Semantic Versioning](http://semver.org/).

## [Unreleased] - TBD
### Added
- Build-time class-cache generation: a `tenup-framework-generate-class-cache` command (installed to `vendor/bin/`) and a `composer generate-class-cache` alias that build the cache in CI without bootstrapping WordPress. See [Build and Deployment](docs/Build-and-Deployment.md) ([#30](https://github.com/10up/wp-framework/issues/30)).
- Hidden admin page (`admin.php?page=tenup-framework-loaders`, `manage_options`) that aggregates every class-loader cache on the site — across all framework copies — and shows each cache's path, status, loaded classes, and an on-demand live-vs-cache staleness check. Admin-only (no front-end overhead) and read-only. Disable with the `tenup_framework_enable_loader_debug` filter or the `TENUP_FRAMEWORK_DISABLE_LOADER_DEBUG` constant. See [Debugging class loaders](docs/Debugging.md).
- The loader debug page reports per-loader timing: how long class **discovery** took (a cache read when cached, a live filesystem scan otherwise) and how long **class lookup** (reflection, instantiation and registration) took. The staleness check also reports how long its live discovery ran, so the cache's saving on a given site is measurable.

### Changed
- **Breaking: the minimum required PHP version is now 8.3** (raised from 8.2). Full native type coverage relies on [typed class constants](https://www.php.net/manual/en/language.oop5.constants.php), which are a PHP 8.3 feature and are a parse error on 8.2. This reverses the lowering to 8.2 made in 1.2.0 ([#8](https://github.com/10up/wp-framework/pull/8)). See the [Upgrade Guide](docs/Upgrade-Guide.md).
- **Breaking (static analysis only): `AbstractPostType::get_name()` and `AbstractTaxonomy::get_name()` now document a `lowercase-string&non-empty-string` return type**, stating what WordPress already requires of a post type or taxonomy key. The native `: string` signature is unchanged, so no subclass breaks and no runtime behaviour differs, but a project running PHPStan may need to narrow how it builds a dynamic key. See the [Upgrade Guide](docs/Upgrade-Guide.md).
- Full native type coverage across `src/`: every return type, parameter, property and class constant now declares a native type, and the generic types of every `array` are documented. Enforced by `tomasvotruba/type-coverage` through `composer run static`, which also no longer ignores `missingType.iterableValue`.
- The class-loader cache is now **read-only at runtime** and opt-in. The framework reads a pre-built cache if present and discovers live otherwise, but never writes one on the server — fixing stale caches that could only be cleared by hand ([#30](https://github.com/10up/wp-framework/issues/30)).
- A corrupt or truncated shipped cache is caught at runtime and the request falls back to a live scan instead of fataling, so a bad cache degrades performance rather than taking the site down. The fallback fires a `tenup_framework_cache_load_failed` action (for logging or alerting) and the loader debug page flags that loader red as "Cache failed to load — running live" instead of reporting it as in use.
- Bumped the cache filename so a cache written by an older version is ignored after upgrade rather than served stale.
- `TENUP_FRAMEWORK_DISABLE_CLASS_CACHE` now forces live discovery (ignores any shipped cache).

### Removed
- Automatic runtime cache generation and its environment gating — `should_use_cache()`, the `production`/`staging` checks, and the `VIP_GO_APP_ENVIRONMENT` handling. Caching is now produced at build time instead.

## [1.2.0] - 2025-03-20
### Changed
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
- **Shared Functionality:** Provides commonly used abstract classes and utilities to reduce boilerplate code in WordPress projects.
- **Extendability:** Built for easy extension. Engineers can subclass or override functionality as needed to tailor it to their projects.
- **Centralized Updates:** Simplifies rolling out updates and new features across projects using this framework.
- **Modern Standards:** Compatible with PHP 8.2+ and adheres to modern development practices.
- **Modern Standards:** Compatible with PHP 8.3+ and adheres to modern development practices.

## Installation

Expand Down
76 changes: 76 additions & 0 deletions bin/tenup-framework-generate-class-cache
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/usr/bin/env php
<?php
/**
* Generate the 10up WP Framework class-loader cache for one or more directories.
*
* Runs at build time (typically in CI) WITHOUT bootstrapping WordPress, and writes a
* cache file into each given directory. At runtime the framework reads that file but
* never rewrites it, so the cache can never go stale on a server (see GitHub issue #30).
*
* Usage:
* tenup-framework-generate-class-cache <dir> [<dir> ...]
*
* Pass the same directory (or directories) you pass to
* TenupFramework\ModuleInitialization::init_classes() — usually your plugin/theme `inc/`.
*
* @package TenupFramework
*/

declare( strict_types = 1 );

namespace TenupFramework\Bin;

use TenupFramework\ModuleInitialization;
use Throwable;

// Locate the Composer autoloader whether this runs from within the package itself
// (development) or installed inside a consumer project's vendor directory. Composer 2.2+
// exposes the path via this global from the generated bin proxy; otherwise fall back to
// the known relative locations.
$tenup_autoloader_loaded = false;

if ( isset( $GLOBALS['_composer_autoload_path'] ) && file_exists( (string) $GLOBALS['_composer_autoload_path'] ) ) {
require $GLOBALS['_composer_autoload_path'];
$tenup_autoloader_loaded = true;
} else {
$tenup_autoload_candidates = [
__DIR__ . '/../vendor/autoload.php', // Running from the package itself.
__DIR__ . '/../../../autoload.php', // Installed at vendor/10up/wp-framework/bin.
];

foreach ( $tenup_autoload_candidates as $tenup_autoload_candidate ) {
if ( file_exists( $tenup_autoload_candidate ) ) {
require $tenup_autoload_candidate;
$tenup_autoloader_loaded = true;
break;
}
}
}

if ( ! $tenup_autoloader_loaded ) {
fwrite( STDERR, "Could not locate the Composer autoloader. Run `composer install` first.\n" );
exit( 1 );
}

// Target directories are the CLI arguments (everything after the script name).
$tenup_directories = array_slice( $argv, 1 );

if ( empty( $tenup_directories ) ) {
fwrite( STDERR, "Usage: tenup-framework-generate-class-cache <dir> [<dir> ...]\n" );
fwrite( STDERR, "Pass one or more directories (the same ones passed to ModuleInitialization::init_classes()).\n" );
exit( 1 );
}

$tenup_exit_code = 0;

foreach ( $tenup_directories as $tenup_directory ) {
try {
$tenup_classes = ModuleInitialization::instance()->generate_cache( $tenup_directory );
fwrite( STDOUT, sprintf( "Cached %d class(es) for %s\n", count( $tenup_classes ), $tenup_directory ) );
} catch ( Throwable $tenup_exception ) {
fwrite( STDERR, sprintf( "Failed to generate cache for %s: %s\n", $tenup_directory, $tenup_exception->getMessage() ) );
$tenup_exit_code = 1;
}
}

exit( $tenup_exit_code );
20 changes: 16 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
"role": "Developer"
}
],
"bin": [
"bin/tenup-framework-generate-class-cache"
],
"autoload": {
"psr-4": {
"TenupFramework\\": "src/"
Expand All @@ -30,7 +33,7 @@
}
},
"require": {
"php": ">=8.2",
"php": ">=8.3",
"spatie/php-structure-discoverer": "^2.2"
},
"require-dev": {
Expand All @@ -41,9 +44,10 @@
"php-stubs/wp-cli-stubs": "^2.11",
"phpstan/phpstan-deprecation-rules": "^2.0",
"10up/phpcs-composer": "^3.0",
"phpcompatibility/php-compatibility": "dev-develop as 9.99.99",
"phpunit/php-code-coverage": "^9.2",
"slevomat/coding-standard": "^8.15"
"slevomat/coding-standard": "^8.15",
"rector/rector": "^2.0",
"tomasvotruba/type-coverage": "^2.0"
},
"scripts": {
"test": "XDEBUG_MODE=coverage ./vendor/bin/phpunit",
Expand All @@ -52,7 +56,15 @@
"static": [
"Composer\\Config::disableProcessTimeout",
"phpstan --memory-limit=1G"
]
],
"rector": [
"./vendor/bin/rector",
"composer run lint-fix"
],
"generate-class-cache": "@php bin/tenup-framework-generate-class-cache"
},
"scripts-descriptions": {
"generate-class-cache": "Generate the class-loader cache for one or more directories, e.g. `composer generate-class-cache -- inc/`."
},
"config": {
"allow-plugins": {
Expand Down
Loading
Loading