Conversation
…m requirement to 8.2
… and regenerate composer.lock
… update composer.lock
…antle-framework dependencies
There was a problem hiding this comment.
Pull request overview
Modernizes the project’s development and CI tooling while migrating the WordPress test setup to Mantle and refactoring the Routes plugin code to align with newer PHP/testing standards.
Changes:
- Replace Travis-based CI with GitHub Actions for tests/linting/Rector (plus shared setup action).
- Migrate and rewrite the test suite to Mantle (
tests/bootstrap.php,tests/RoutesTest.php). - Update
Routes.phpformatting/docs and update Composer + tooling configs (ECS/Rector/PHPUnit).
Reviewed changes
Copilot reviewed 17 out of 20 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
Routes.php |
Refactors/modernizes the core plugin class and documentation. |
composer.json |
Raises PHP requirement and adds dev tooling/testing dependencies and scripts. |
phpunit.xml |
Updates PHPUnit configuration for newer PHPUnit versions and stricter failure settings. |
tests/bootstrap.php |
Replaces WP test bootstrap with Mantle bootstrap + SQLite setup. |
tests/RoutesTest.php |
Rewrites route behavior tests for Mantle’s Integration_Test_Case. |
tests/Supports/single.php |
Adds a fixture file used by tests. |
ecs.php / rector.php |
Adds code style and automated refactoring configs. |
.github/workflows/php-unit-tests.yml |
Adds PHPUnit workflow matrix. |
.github/workflows/php-lint.yml |
Adds parallel-lint workflow. |
.github/workflows/php-rector.yml |
Adds Rector workflow. |
.github/actions/setup/action.yml |
Adds composite action for PHP + Composer setup. |
.gitattributes |
Updates export-ignore list for release archives. |
README.md |
Updates badges/formatting. |
.gitignore |
Updates ignored files for vendor/build/tmp/test artifacts. |
Removed: .travis.yml, bin/install-wp-tests.sh, tests/test-routes.php, tests/single.php |
Removes Travis + legacy WP test scaffolding in favor of Mantle + Actions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| function cache_sqlite_develop(string $cacheDir): void | ||
| { | ||
| $sqliteZip = $cacheDir . '/sqlite-database-integration-main.zip'; | ||
|
|
||
| if (is_file($sqliteZip)) { | ||
| return; | ||
| } | ||
|
|
||
| if (!is_dir($cacheDir)) { | ||
| mkdir($cacheDir, 0755, true); | ||
| } | ||
|
|
||
| // Download develop branch, extract, rename folder, re-zip with correct name | ||
| $url = 'https://github.com/WordPress/sqlite-database-integration/archive/refs/heads/develop.zip'; | ||
| shell_exec('curl -sL ' . escapeshellarg($url) . ' -o ' . escapeshellarg("$cacheDir/tmp.zip")); | ||
| shell_exec('unzip -q ' . escapeshellarg("$cacheDir/tmp.zip") . ' -d ' . escapeshellarg($cacheDir)); | ||
| rename("$cacheDir/sqlite-database-integration-develop", "$cacheDir/sqlite-database-integration-main"); | ||
| shell_exec('cd ' . escapeshellarg($cacheDir) . ' && zip -rq sqlite-database-integration-main.zip sqlite-database-integration-main'); | ||
| shell_exec('rm -rf ' . escapeshellarg("$cacheDir/tmp.zip") . ' ' . escapeshellarg("$cacheDir/sqlite-database-integration-main")); |
There was a problem hiding this comment.
cache_sqlite_develop() relies on shell_exec() plus external tools (curl, unzip, zip, rm) with no error handling. This can make test bootstrapping fail in environments where these binaries or shell_exec are unavailable/disabled. Prefer using PHP (e.g., ZipArchive/curl extension) or add explicit checks and actionable failures when commands fail.
| function cache_sqlite_develop(string $cacheDir): void | |
| { | |
| $sqliteZip = $cacheDir . '/sqlite-database-integration-main.zip'; | |
| if (is_file($sqliteZip)) { | |
| return; | |
| } | |
| if (!is_dir($cacheDir)) { | |
| mkdir($cacheDir, 0755, true); | |
| } | |
| // Download develop branch, extract, rename folder, re-zip with correct name | |
| $url = 'https://github.com/WordPress/sqlite-database-integration/archive/refs/heads/develop.zip'; | |
| shell_exec('curl -sL ' . escapeshellarg($url) . ' -o ' . escapeshellarg("$cacheDir/tmp.zip")); | |
| shell_exec('unzip -q ' . escapeshellarg("$cacheDir/tmp.zip") . ' -d ' . escapeshellarg($cacheDir)); | |
| rename("$cacheDir/sqlite-database-integration-develop", "$cacheDir/sqlite-database-integration-main"); | |
| shell_exec('cd ' . escapeshellarg($cacheDir) . ' && zip -rq sqlite-database-integration-main.zip sqlite-database-integration-main'); | |
| shell_exec('rm -rf ' . escapeshellarg("$cacheDir/tmp.zip") . ' ' . escapeshellarg("$cacheDir/sqlite-database-integration-main")); | |
| /** | |
| * Recursively add a directory to a ZipArchive. | |
| * | |
| * @param \ZipArchive $zip | |
| * @param string $directory Absolute path to the directory to add. | |
| * @param string $baseInArchive Path prefix inside the archive. | |
| */ | |
| function cache_sqlite_add_directory_to_zip(\ZipArchive $zip, string $directory, string $baseInArchive = ''): void | |
| { | |
| $directory = rtrim($directory, DIRECTORY_SEPARATOR); | |
| if (!is_dir($directory)) { | |
| return; | |
| } | |
| $items = scandir($directory); | |
| if ($items === false) { | |
| throw new \RuntimeException("Failed to read directory: {$directory}"); | |
| } | |
| foreach ($items as $item) { | |
| if ($item === '.' || $item === '..') { | |
| continue; | |
| } | |
| $fullPath = $directory . DIRECTORY_SEPARATOR . $item; | |
| $localName = ltrim($baseInArchive . '/' . $item, '/'); | |
| if (is_dir($fullPath)) { | |
| if (!$zip->addEmptyDir($localName)) { | |
| throw new \RuntimeException("Failed to add directory to zip: {$localName}"); | |
| } | |
| cache_sqlite_add_directory_to_zip($zip, $fullPath, $localName); | |
| } elseif (is_file($fullPath)) { | |
| if (!$zip->addFile($fullPath, $localName)) { | |
| throw new \RuntimeException("Failed to add file to zip: {$fullPath}"); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Recursively remove a directory and its contents. | |
| */ | |
| function cache_sqlite_rrmdir(string $directory): void | |
| { | |
| if (!is_dir($directory)) { | |
| return; | |
| } | |
| $items = scandir($directory); | |
| if ($items === false) { | |
| throw new \RuntimeException("Failed to read directory for removal: {$directory}"); | |
| } | |
| foreach ($items as $item) { | |
| if ($item === '.' || $item === '..') { | |
| continue; | |
| } | |
| $path = $directory . DIRECTORY_SEPARATOR . $item; | |
| if (is_dir($path)) { | |
| cache_sqlite_rrmdir($path); | |
| } elseif (is_file($path)) { | |
| if (!unlink($path)) { | |
| throw new \RuntimeException("Failed to delete file: {$path}"); | |
| } | |
| } | |
| } | |
| if (!rmdir($directory)) { | |
| throw new \RuntimeException("Failed to remove directory: {$directory}"); | |
| } | |
| } | |
| function cache_sqlite_develop(string $cacheDir): void | |
| { | |
| $sqliteZip = $cacheDir . '/sqlite-database-integration-main.zip'; | |
| // If the cached zip already exists, assume it's valid and skip work. | |
| if (is_file($sqliteZip) && filesize($sqliteZip) > 0) { | |
| return; | |
| } | |
| if (!is_dir($cacheDir)) { | |
| if (!mkdir($cacheDir, 0755, true) && !is_dir($cacheDir)) { | |
| throw new \RuntimeException("Failed to create cache directory: {$cacheDir}"); | |
| } | |
| } | |
| $tmpZip = $cacheDir . '/tmp.zip'; | |
| // Download develop branch, extract, rename folder, re-zip with correct name. | |
| $url = 'https://github.com/WordPress/sqlite-database-integration/archive/refs/heads/develop.zip'; | |
| $zipData = @file_get_contents($url); | |
| if ($zipData === false) { | |
| // Fallback to curl extension if available. | |
| if (function_exists('curl_init')) { | |
| $ch = curl_init($url); | |
| curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | |
| curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); | |
| curl_setopt($ch, CURLOPT_FAILONERROR, true); | |
| $zipData = curl_exec($ch); | |
| $curlError = curl_error($ch); | |
| curl_close($ch); | |
| if ($zipData === false) { | |
| throw new \RuntimeException("Failed to download SQLite develop zip via curl: {$curlError}"); | |
| } | |
| } else { | |
| throw new \RuntimeException( | |
| 'Failed to download SQLite develop zip: allow_url_fopen is disabled and curl extension is unavailable.' | |
| ); | |
| } | |
| } | |
| if (file_put_contents($tmpZip, $zipData) === false) { | |
| throw new \RuntimeException("Failed to write temporary zip file: {$tmpZip}"); | |
| } | |
| if (!class_exists(\ZipArchive::class)) { | |
| throw new \RuntimeException( | |
| 'ZipArchive extension is required to prepare the SQLite develop cache but is not available.' | |
| ); | |
| } | |
| $zip = new \ZipArchive(); | |
| $openResult = $zip->open($tmpZip); | |
| if ($openResult !== true) { | |
| throw new \RuntimeException("Failed to open downloaded zip file ({$tmpZip}); ZipArchive error code: {$openResult}"); | |
| } | |
| if (!$zip->extractTo($cacheDir)) { | |
| $zip->close(); | |
| throw new \RuntimeException("Failed to extract downloaded zip to cache directory: {$cacheDir}"); | |
| } | |
| $zip->close(); | |
| $developDir = $cacheDir . '/sqlite-database-integration-develop'; | |
| $mainDir = $cacheDir . '/sqlite-database-integration-main'; | |
| if (!is_dir($developDir)) { | |
| throw new \RuntimeException( | |
| "Expected extracted directory not found: {$developDir}" | |
| ); | |
| } | |
| // Remove any stale main directory before renaming. | |
| if (is_dir($mainDir)) { | |
| cache_sqlite_rrmdir($mainDir); | |
| } | |
| if (!rename($developDir, $mainDir)) { | |
| throw new \RuntimeException( | |
| "Failed to rename {$developDir} to {$mainDir}" | |
| ); | |
| } | |
| // Create the expected sqlite-database-integration-main.zip. | |
| $zip = new \ZipArchive(); | |
| $openResult = $zip->open($sqliteZip, \ZipArchive::CREATE | \ZipArchive::OVERWRITE); | |
| if ($openResult !== true) { | |
| throw new \RuntimeException( | |
| "Failed to create SQLite cache zip ({$sqliteZip}); ZipArchive error code: {$openResult}" | |
| ); | |
| } | |
| cache_sqlite_add_directory_to_zip($zip, $mainDir, 'sqlite-database-integration-main'); | |
| $zip->close(); | |
| // Clean up temporary files and directories. | |
| if (is_file($tmpZip) && !unlink($tmpZip)) { | |
| throw new \RuntimeException("Failed to delete temporary zip file: {$tmpZip}"); | |
| } | |
| if (is_dir($mainDir)) { | |
| cache_sqlite_rrmdir($mainDir); | |
| } |
|
|
||
| - name: Run Rector checks | ||
| if: steps.changed-files.outputs.all_changed_files != '' | ||
| run: ./vendor/bin/rector process --dry-run ${{ steps.changed-files.outputs.files }} |
There was a problem hiding this comment.
This workflow checks all_changed_files in the if: condition, but the run: command uses steps.changed-files.outputs.files, which isn’t produced by tj-actions/changed-files (so Rector may run with an empty file list). Use the same all_changed_files output (optionally filtered to PHP files) in the Rector command.
| run: ./vendor/bin/rector process --dry-run ${{ steps.changed-files.outputs.files }} | |
| run: ./vendor/bin/rector process --dry-run ${{ steps.changed-files.outputs.all_changed_files }} |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…eRoutePrecedingSlash
Code Quality Improvements
This PR modernizes the codebase with updated tooling, CI/CD, and a refactored
Routesclass.Changes
CI / Tooling
ecs.php(Easy Coding Standard) andrector.phpconfigs.gitignoreand.gitattributesTesting
tests/RoutesTest.phpwith updated test casesphpunit.xmlandtests/bootstrap.phptests/test-routes.phpandbin/install-wp-tests.shRoutes class
Routes.phpfor improved readability, maintainability, and documentationComposer
composer.jsonwith new dependencies and scripts