Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 5 additions & 3 deletions .github/workflows/qa.yml
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,13 @@ jobs:
id: psalm
run: |
set +e
vendor/bin/psalm --output-format=json --show-info=false > psalm.json 2>&1
# Stderr is redirected to /dev/null to prevent progress output from corrupting JSON
vendor/bin/psalm --output-format=json --show-info=false > psalm.json 2>/dev/null
EXIT_CODE=$?

# Generate SARIF for GitHub Security
vendor/bin/psalm --output-format=sarif --show-info=false > psalm.sarif 2>&1 || true
# Stderr is redirected to /dev/null to prevent progress output from corrupting SARIF
vendor/bin/psalm --output-format=sarif --show-info=false > psalm.sarif 2>/dev/null || true

ERRORS=$(jq 'length' psalm.json 2>/dev/null || echo "0")
echo "errors=${ERRORS}" >> $GITHUB_OUTPUT
Expand All @@ -200,7 +202,7 @@ jobs:

- name: Upload Psalm SARIF
if: always()
uses: github/codeql-action/upload-sarif@v3
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: psalm.sarif
category: psalm
Expand Down
136 changes: 136 additions & 0 deletions AUDIT-PERFORMANCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Performance Audit Report

This report details the findings of a performance audit conducted on the Core PHP Framework. The audit focused on identifying performance issues and optimization opportunities across several areas of the application.

## Database Performance

### N+1 Queries

Several areas were identified as having a high potential for N+1 query issues, where a loop over a set of results leads to additional queries being executed for each item in the loop. These should be investigated to ensure eager loading is used where appropriate.

- **`src/Core/Config/ConfigResolver.php`**: A loop over `ConfigKey` models.
- **`src/Core/Front/Admin/AdminMenuRegistry.php`**: Loops over menu item providers.
- **`src/Core/Activity/View/Blade/admin/activity-feed.blade.php`**: A loop over activities in a feed. This is a common source of N+1 issues if related models (like the user who performed the action) are not eager-loaded.
- **`src/Core/Seo/Analytics/SeoScoreTrend.php`**: A loop over SEO issues related to a record.

**Recommendation:** Review these loops and use Laravel's `with()` method to eager-load any relationships that are accessed within the loop.

### Missing Indexes

A full analysis of missing indexes requires runtime query analysis, which is beyond the scope of this static audit.

**Recommendation:** Use a tool like Laravel Telescope or a database-level query analyzer in a development or staging environment to identify slow queries and add indexes where necessary.

### Large Result Sets

The audit identified several places where the `::all()` Eloquent method is used, which can lead to high memory usage if the tables contain a large number of rows.

- **`src/Core/Config/ConfigResolver.php`**: `ConfigKey::all()`
- **`src/Core/Config/Console/ConfigPrimeCommand.php`**: `\Core\Tenant\Models\Workspace::all()`
- **`src/Core/Cdn/Console/CdnPurge.php`**: `\Core\Tenant\Models\Workspace::all()`
- **`src/Core/Seo/Validation/CanonicalUrlValidator.php`**: `SeoMetadata::all()`

**Recommendation:** For operations that process a large number of records, use chunking (`chunk()`, `chunkById()`) or cursors (`cursor()`) to process records in smaller batches, reducing memory consumption.

### Inefficient Joins

Static analysis cannot reliably identify inefficient joins. This requires runtime query analysis.

**Recommendation:** Use a query analysis tool to inspect the performance of database queries, particularly those involving multiple joins.

### Connection Pooling

The framework is a Laravel package and relies on the consuming application for its database configuration. As such, connection pooling is outside the direct control of the package.

**Recommendation:** Ensure that the consuming application is configured to use persistent database connections (e.g., by setting `PDO::ATTR_PERSISTENT => true` in the database configuration's `options` array) if appropriate for the environment.

## Memory Usage

### Memory Leaks

Static analysis is not sufficient to detect memory leaks, which typically require runtime profiling.

**Recommendation:** Use a profiler like Blackfire.io or Xdebug to analyze the memory usage of the application under load and identify any potential leaks.

### Large Object Loading

As noted in the "Large Result Sets" section, there are several places where `::all()` is used, which can lead to high memory usage.

**Recommendation:** Refactor these queries to use chunking or cursors.

### Cache Efficiency

The framework provides several caching mechanisms for its features, including the admin menu, workspace data, and service discovery. These are configurable and can be enabled or disabled as needed. The main application caching strategy is left to the consuming application.

**Recommendation:**
- The consuming application should use a fast, dedicated caching driver like Redis or Memcached in production.
- Monitor cache hit rates to ensure that the caches are being used effectively.
- The TTLs for the various framework caches (`admin_menu`, `workspace_cache`) should be tuned based on the application's specific needs for data freshness.

## Concurrency

### Blocking Operations

The audit did not identify any obvious blocking I/O operations in the main request-response cycle. The framework makes good use of Laravel's queuing system to offload long-running tasks.

### Lock Contention

Static analysis cannot reliably identify lock contention issues.

**Recommendation:** If the application experiences concurrency issues, use profiling tools to identify and address any lock contention.

### Thread Pool Sizing

This is not applicable to a typical PHP-FPM execution model.

### Async Opportunities

The framework already makes good use of asynchronous jobs for tasks like:

- CDN asset pushing (`PushAssetToCdn`)
- Media processing (`GenerateThumbnail`, `ProcessMediaConversion`)
- SEO image generation (`GenerateOgImageJob`)

**Recommendation:** Continue to use queued jobs for any new long-running or blocking tasks that are added to the application.

## API Performance

### Response Times

Response times can only be measured at runtime.

**Recommendation:** Implement monitoring for API endpoints to track P50, P95, and P99 response times. Use this data to identify slow endpoints that require optimization.

### Payload Sizes

Static analysis cannot determine the size of API payloads.

**Recommendation:** Review the data returned by API endpoints and remove any unnecessary fields. Use a tool like Laravel's API Resources to control the shape of API responses. Consider implementing pagination for any endpoints that can return large lists of data.

### Caching Headers

The framework includes a middleware for security-related headers (`SecurityHeaders.php`), but it does not include a centralized middleware for performance-related caching headers like `Cache-Control`, `ETag`, or `Last-Modified`.

**Recommendation:** Implement a middleware to add appropriate caching headers to API responses, especially for data that does not change frequently. This can significantly reduce the load on the server and improve the user experience.

## Build/Deploy Performance

### Build Time

The project uses Vite for its frontend build process. Vite is known for its speed, so the build times are likely to be good.

**Recommendation:** For very large applications, the build process can be parallelized in a CI/CD pipeline if necessary.

### Asset Size

The frontend dependencies are managed in `package.json` and appear to be standard and reasonably sized. However, without a deep analysis of the frontend code, it is difficult to assess the final bundle size.

**Recommendation:**
- Add a bundle analysis tool (e.g., `rollup-plugin-visualizer`) to the build process to visualize the size of the generated JavaScript bundles. This can help identify large dependencies that could be code-split or replaced.
- Ensure that assets like images and fonts are optimized for the web.

### Cold Start

This is not a primary concern for a PHP application running under a service like PHP-FPM.

**Recommendation:** Ensure that PHP's Opcache is enabled and properly configured in production to avoid the overhead of parsing and compiling PHP scripts on every request.
Loading