# Performance Optimization Standards for PHP
This document outlines the coding standards focusing specifically on performance optimization in PHP projects. It aims to provide guidelines and best practices to improve application speed, responsiveness, and resource utilization. These standards should be followed to create efficient, scalable, and maintainable PHP applications.
## 1. Database Optimization
Efficient database interactions are critical for PHP application performance.
### 1.1. Indexing Strategies
**Do This:** Properly index database tables based on query patterns.
**Don't Do This:** Neglect indexing on frequently queried columns, leading to slow query execution times.
**Why:** Indexes allow the database to quickly locate specific rows without scanning the entire table.
"""php
// Example: Adding an index to the 'email' column in the 'users' table
$sql = "ALTER TABLE users ADD INDEX email_index (email);";
// Or using Laravel's migration:
/*
Schema::table('users', function (Blueprint $table) {
$table->index('email');
});
*/
"""
**Anti-Pattern:** Over-indexing, which can slow down write operations and consume unnecessary storage space.
### 1.2. Query Optimization
**Do This:** Use "EXPLAIN" to analyze query performance and identify bottlenecks. Optimize queries by using appropriate "WHERE" clauses, "JOIN" conditions, and avoiding "SELECT *".
**Don't Do This:** Writing complex queries without understanding their performance implications.
**Why:** Optimizing queries reduces the amount of data that the database needs to process.
"""php
// Example: Using EXPLAIN to analyze a query
$sql = "EXPLAIN SELECT * FROM products WHERE category_id = 1 AND price > 100;";
// Optimized query:
$sql = "SELECT id, name, price FROM products WHERE category_id = 1 AND price > 100;";
"""
**Anti-Pattern:** Using ORM (Object-Relational Mapper) without understanding the generated SQL can lead to inefficient database queries. Always inspect the queries generated by your ORM.
### 1.3. Connection Management
**Do This:** Use persistent database connections to reduce connection overhead (when appropriate).
**Don't Do This:** Opening and closing database connections for every request.
**Why:** Establishing a new database connection is a resource-intensive operation.
"""php
// Example: PDO persistent connection
$dsn = "mysql:host=localhost;dbname=mydb";
$options = array(PDO::ATTR_PERSISTENT => true);
try {
$pdo = new PDO($dsn, 'user', 'password', $options);
} catch (PDOException $e) {
echo "Connection failed: " . $e->getMessage();
}
// Using Laravel's configuration to set persistent connections
/*
'mysql' => [
'driver' => 'mysql',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
PDO::ATTR_PERSISTENT => true, // Enabling persistent connections
]) : [],
],
*/
"""
**Anti-Pattern:** Holding database connections open longer than necessary, which can exhaust database resources.
### 1.4. Data Caching
**Do This:** Implement caching mechanisms for frequently accessed data sets, using technologies like Redis or Memcached.
**Don't Do This:** Repeatedly querying the database for the same data within a short period.
**Why:** Caching reduces the load on the database and speeds up data retrieval.
"""php
// Example: Using Redis for caching
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$cacheKey = 'user:' . $userId;
$userData = $redis->get($cacheKey);
if (!$userData) {
// Fetch data from the database
$userData = fetchUserDataFromDatabase($userId);
$redis->set($cacheKey, $userData, 3600); // Cache for 1 hour
}
// Using Laravel's cache facade
/*
$value = Cache::remember('users', 60, function () {
return DB::table('users')->get();
});
*/
"""
**Anti-Pattern:** Caching data indefinitely without proper cache invalidation strategies, leading to stale data.
### 1.5 Prepared Statements
**Do This:** Employ prepared statements to prevent SQL injection and improve query performance.
**Don't Do This:** Constructing SQL queries by directly concatenating user inputs.
**Why:** Prepared statements precompile SQL queries and reuse them with different parameters.
"""php
// Example: Using prepared statements in PDO
$stmt = $pdo->prepare("SELECT * FROM products WHERE category_id = :category_id AND price > :price");
$stmt->bindParam(':category_id', $categoryId, PDO::PARAM_INT);
$stmt->bindParam(':price', $price, PDO::PARAM_INT);
$stmt->execute();
$results = $stmt->fetchAll();
"""
**Anti-Pattern:** Relying solely on input sanitization without prepared statements.
## 2. Code Optimization
Efficiently written PHP code significantly impacts application performance.
### 2.1. Algorithm Selection
**Do This:** Choose appropriate algorithms and data structures for specific tasks.
**Don't Do This:** Using inefficient algorithms that increase execution time, especially for large datasets.
**Why:** The choice of algorithm can drastically affect performance.
"""php
// Example: Using array_search vs. isset for checking array key existence
$array = ['key1' => 'value1', 'key2' => 'value2', 'key3' => 'value3'];
// Inefficient:
$startTime = microtime(true);
$exists = array_search('key1', array_keys($array));
$endTime = microtime(true);
$arraySearchTime = $endTime - $startTime;
// Efficient:
$startTime = microtime(true);
$exists = isset($array['key1']);
$endTime = microtime(true);
$issetTime = $endTime - $startTime;
echo "array_search time: " . $arraySearchTime . "\n"; //array_search time: 7.9624176025391E-6
echo "isset time: " . $issetTime . "\n"; //isset time: 9.5367431640625E-7
"""
**Anti-Pattern:** Ignoring algorithmic complexity when developing functions, leading to performance bottlenecks as data volumes grow.
### 2.2. Loop Optimization
**Do This:** Minimize operations inside loops and cache frequently used values outside the loop.
**Don't Do This:** Performing calculations or database queries within loops unnecessarily.
**Why:** Reducing the number of operations executed within a loop decreases execution time.
"""php
// Inefficient:
$arrayLength = count($myArray);
for ($i = 0; $i < count($myArray); $i++) {
echo $myArray[$i] . " - Length: " . $arrayLength . "\n";
}
// Efficient:
$arrayLength = count($myArray);
for ($i = 0; $i < $arrayLength; $i++) {
echo $myArray[$i] . " - Length: " . $arrayLength . "\n";
}
"""
**Anti-Pattern:** Using "foreach" loops when a simple "for" loop would suffice, especially when dealing with numerical indices.
### 2.3. String Manipulation
**Do This:** Use appropriate string functions and avoid excessive string concatenation. Consider using "StringBuilder" techniques (if available or emulated) for complex string building.
**Don't Do This:** Performing inefficient string operations, especially within loops.
**Why:** String operations can be resource-intensive, and choosing the right function can improve performance.
"""php
// Inefficient:
$string = '';
for ($i = 0; $i < 1000; $i++) {
$string .= 'some text';
}
// Efficient:
$string = str_repeat('some text', 1000);
"""
**Anti-Pattern:** Repeatedly concatenating strings inside a loop using the "." operator, which creates multiple string copies.
### 2.4. Function Calls
**Do This:** Minimize the number of function calls and avoid unnecessary function definitions. Use built-in functions where possible.
**Don't Do This:** Creating overly complex function structures that add overhead to execution.
**Why:** Function calls introduce overhead due to stack manipulation and context switching.
"""php
// Inefficient:
function add($a, $b) {
return $a + $b;
}
$result = add(5, 3);
// Efficient: (In this specific simple case)
$result = 5 + 3;
"""
**Anti-Pattern:** Overusing functions for simple operations, especially when the function body is very short.
### 2.5. Autoloading Optimization
**Do This:** Optimize autoloading mechanisms to load only the necessary classes.
**Don't Do This:** Loading unnecessary classes, which increases memory usage and startup time.
**Why:** Efficient autoloading ensures that only the required classes are loaded, reducing overhead.
"""php
// Example: Using Composer's autoloader
require __DIR__ . '/vendor/autoload.php';
// In Composer's composer.json file:
/*
"autoload": {
"psr-4": {
"MyApp\\": "src/"
}
}
*/
"""
**Anti-Pattern:** Autoloading all classes at once, which increases memory usage and startup time.
## 3. Caching Strategies
Caching plays a crucial role in improving application performance.
### 3.1. Opcode Caching
**Do This:** Enable opcode caching (e.g., using OPcache) to cache compiled PHP code.
**Don't Do This:** Running PHP applications without opcode caching enabled.
**Why:** Opcode caching stores the compiled bytecode of PHP scripts, reducing the need for recompilation on subsequent requests.
"""php
; Configuration for OPcache (in php.ini)
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.validate_timestamps=0 ;Only for production - disable timestamp validation
"""
**Anti-Pattern:** Deploying PHP applications to production without enabling opcode caching.
### 3.2. Data Caching
**Do This:** Use data caching to store the results of expensive operations (e.g., database queries, API calls).
**Don't Do This:** Repeatedly performing the same expensive operations for each request.
**Why:** Data caching reduces the load on backend services and speeds up data retrieval.
"""php
// Example: Using Memcached for data caching
$memcached = new Memcached();
$memcached->addServer('localhost', 11211);
$key = 'my_data';
$data = $memcached->get($key);
if ($data === false) {
// Fetch data from the database or API
$data = fetchDataFromSource();
$memcached->set($key, $data, 3600); // Cache for 1 hour
}
"""
**Anti-Pattern:** Caching sensitive data without proper security measures.
### 3.3. Page Caching
**Do This:** Implement full-page caching for static or semi-static content.
**Don't Do This:** Dynamically generating the same content for every request.
**Why:** Page caching serves pre-rendered HTML pages, significantly reducing server load.
"""php
// Example: Basic page caching
$cacheFile = 'cache/page.html';
$cacheTime = 3600; // Cache for 1 hour
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < $cacheTime)) {
readfile($cacheFile);
exit;
}
ob_start();
// Generate the dynamic content here
echo "Dynamic Content";
$content = ob_get_contents();
ob_end_clean();
file_put_contents($cacheFile, $content);
echo $content;
"""
**Anti-Pattern:** Caching dynamic content without proper cache invalidation, leading to stale content being served.
### 3.4. CDN (Content Delivery Network)
**Do This:** Use a CDN to serve static assets (e.g., images, CSS, JavaScript) from geographically distributed servers.
**Don't Do This:** Serving all static assets from a single server.
**Why:** CDNs reduce latency and improve loading times for users in different locations.
"""html
"""
**Anti-Pattern:** Not leveraging CDNs for static assets in geographically diverse applications.
## 4. Resource Management
Efficient resource management is crucial for preventing performance bottlenecks.
### 4.1. Memory Management
**Do This:** Free up memory by unsetting variables when they are no longer needed.
**Don't Do This:** Holding onto large data structures in memory for extended periods.
**Why:** Releasing memory reduces memory consumption and prevents memory leaks.
"""php
// Example: Unsetting a variable
$data = fetchData();
// ... use $data ...
unset($data);
"""
**Anti-Pattern:** Creating large arrays or objects and not releasing them, leading to memory exhaustion.
### 4.2. File I/O
**Do This:** Minimize file I/O operations and use buffered I/O for large files.
**Don't Do This:** Repeatedly opening and closing files for small read/write operations.
**Why:** File I/O is a relatively slow operation, and reducing the number of I/O operations improves performance.
"""php
// Example: Buffered file reading
$handle = fopen("inputfile.txt", "rb");
if ($handle) {
while (($line = fgets($handle)) !== false) {
// process the line read.
echo $line;
}
fclose($handle);
} else {
// error opening the file.
}
"""
**Anti-Pattern:** Reading entire files into memory when only a small portion is needed.
### 4.3. External Processes
**Do This:** Minimize the use of external processes and use asynchronous processing where appropriate.
**Don't Do This:** Blocking PHP execution by waiting for external processes to complete.
**Why:** External processes can introduce significant overhead and increase response times.
"""php
// Example: Using asynchronous processing with pcntl_fork
if (pcntl_fork() == 0) {
// Child process
exec('long_running_script.sh');
exit(0);
} else {
// Parent process
// Continue processing the request
echo "Processing started in the background.";
}
"""
**Anti-Pattern:** Synchronously executing long-running external processes that block PHP execution. Consider using message queues (e.g., RabbitMQ, Redis Queue) for background processing.
### 4.4. Error Handling
**Do This:** Implement efficient error handling and logging mechanisms. Log errors to files instead of displaying them.
**Don't Do This:** Displaying verbose error messages to users in production, which can expose sensitive information and impact performance.
**Why:** Efficient error handling prevents unexpected crashes and helps diagnose issues.
"""php
// Example: Using try-catch blocks for error handling
try {
// Code that may throw an exception
$result = performOperation();
} catch (Exception $e) {
// Log the error
error_log("Error: " . $e->getMessage());
// Display a user-friendly error message
echo "An error occurred. Please try again later.";
}
"""
**Anti-Pattern:** Ignoring errors or displaying verbose error messages to users in production environments.
## 5. Latest PHP Feature Utilization
Leverage new features in recent PHP releases to optimize performance.
### 5.1. Preloading (PHP 7.4+)
**Do This**: Utilize PHP preloading to load commonly used code into memory when the server starts.
**Don't Do This**: Ignore preloading, especially in large applications with frequently used components.
**Why**: Preloading avoids the overhead of repeatedly loading and linking files on each request, improving overall performance and reducing latency.
"""php
; In php.ini
opcache.preload=/path/to/preload.php
"""
"""php
cache[$key] ?? null;
return $weakRef ? $weakRef->get() : null;
}
public function set(object $obj): void {
$key = spl_object_id($obj);
$this->cache[$key] = WeakReference::create($obj);
}
}
$cache = new MyCache();
$obj = new stdClass();
$cache->set($obj);
$retrievedObj = $cache->get($obj);
var_dump($retrievedObj === $obj); // bool(true)
unset($obj); // Object is now only referenced by the WeakReference
var_dump($cache->get($obj)); // NULL (object has been garbage collected)
"""
**Anti-Pattern**: Using strong references for objects that are only needed temporarily, which can lead to memory leaks.
### 5.3. FFI (Foreign Function Interface) (PHP 7.4+)
**Do This**: Consider utilizing FFI for performance-critical sections to leverage native libraries for specific tasks.
**Don't Do This**: Overuse FFI; use it judiciously for operations that PHP struggles with.
**Why**: FFI allows calling functions written in C/C++ directly from PHP, which can provide significant performance improvements for certain operations.
"""php
abs(-5);
var_dump($abs); // int(5)
"""
**Anti-Pattern**: Using FFI for tasks that can be efficiently handled by PHP itself, adding unnecessary complexity.
### 5.4 Attributes (PHP 8.0+)
**Do This**: Use attributes (annotations) for configuration and metadata instead of parsing docblocks.
**Don't Do This**: Rely heavily on docblock parsing for configuration, which adds overhead.
**Why**: Attributes provide a more efficient way to add metadata to classes, methods, properties, and functions compared to parsing docblocks.
"""php
getAttributes(MyAttribute::class);
foreach ($attributes as $attribute) {
$instance = $attribute->newInstance();
echo $instance->value . "\n"; //Output: Example
}
"""
**Anti-Pattern**: Still relying on parsing docblocks for configuration when attributes offer a more efficient alternative.
### 5.5. JIT (Just-In-Time Compilation) (PHP 8.0+)
**Do This**: Enable JIT compilation in PHP 8+ to boost performance, particularly for CPU-intensive tasks. Experiment with different JIT modes for optimal performance in your application.
**Don't Do This**: Assume JIT is a magic bullet; profile your application understand if it is actually helpful, and configure it correctly.
**Why**: JIT compilation can significantly improve performance by compiling frequently executed code into machine code at runtime.
"""ini
; In php.ini
opcache.enable=1
opcache.jit_buffer_size=128M
opcache.jit=1235 ; Enable JIT (example configuration)
"""
**Anti-Pattern**: Enabling JIT without proper configuration or without profiling to ensure it actually improves performance. Enabling it without enough "opcache.jit_buffer_size" will likely cause problems.
These standards serve as a comprehensive guide to optimize PHP applications for peak performance. By adhering to these principles, developers can create applications that are not only fast and responsive but also maintainable and scalable. Remember that continuous profiling and monitoring are essential to identify and address performance bottlenecks as applications evolve.
danielsogl
Created Mar 6, 2025
This guide explains how to effectively use .clinerules
with Cline, the AI-powered coding assistant.
The .clinerules
file is a powerful configuration file that helps Cline understand your project's requirements, coding standards, and constraints. When placed in your project's root directory, it automatically guides Cline's behavior and ensures consistency across your codebase.
Place the .clinerules
file in your project's root directory. Cline automatically detects and follows these rules for all files within the project.
# Project Overview project: name: 'Your Project Name' description: 'Brief project description' stack: - technology: 'Framework/Language' version: 'X.Y.Z' - technology: 'Database' version: 'X.Y.Z'
# Code Standards standards: style: - 'Use consistent indentation (2 spaces)' - 'Follow language-specific naming conventions' documentation: - 'Include JSDoc comments for all functions' - 'Maintain up-to-date README files' testing: - 'Write unit tests for all new features' - 'Maintain minimum 80% code coverage'
# Security Guidelines security: authentication: - 'Implement proper token validation' - 'Use environment variables for secrets' dataProtection: - 'Sanitize all user inputs' - 'Implement proper error handling'
Be Specific
Maintain Organization
Regular Updates
# Common Patterns Example patterns: components: - pattern: 'Use functional components by default' - pattern: 'Implement error boundaries for component trees' stateManagement: - pattern: 'Use React Query for server state' - pattern: 'Implement proper loading states'
Commit the Rules
.clinerules
in version controlTeam Collaboration
Rules Not Being Applied
Conflicting Rules
Performance Considerations
# Basic .clinerules Example project: name: 'Web Application' type: 'Next.js Frontend' standards: - 'Use TypeScript for all new code' - 'Follow React best practices' - 'Implement proper error handling' testing: unit: - 'Jest for unit tests' - 'React Testing Library for components' e2e: - 'Cypress for end-to-end testing' documentation: required: - 'README.md in each major directory' - 'JSDoc comments for public APIs' - 'Changelog updates for all changes'
# Advanced .clinerules Example project: name: 'Enterprise Application' compliance: - 'GDPR requirements' - 'WCAG 2.1 AA accessibility' architecture: patterns: - 'Clean Architecture principles' - 'Domain-Driven Design concepts' security: requirements: - 'OAuth 2.0 authentication' - 'Rate limiting on all APIs' - 'Input validation with Zod'
# Deployment and DevOps Standards for PHP This document outlines the necessary standards for effective deployment and DevOps practices within PHP projects. It focuses on establishing reliable, automated, and scalable processes that align with modern PHP development, emphasizing continuous integration, continuous delivery, automated testing, and production environment configuration. ## 1. Build Processes & CI/CD ### 1.1. Continuous Integration/Continuous Delivery (CI/CD) * **Do This:** Implement a CI/CD pipeline for every PHP project, regardless of size. * **Don't Do This:** Manually deploy code changes to production servers without automated testing or rollback strategies. **Explanation:** CI/CD automates testing and deployment, reducing human error and accelerating release cycles. Automated pipelines offer faster feedback loops, enabling developers to efficiently address production issues. **Automated Pipeline Configuration (GitHub Actions Example):** """yaml # .github/workflows/deploy.yml name: Deploy to Production on: push: branches: - main jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: '8.2' # Updated to PHP 8.2 extensions: mbstring, intl, dom, pdo, pdo_mysql, zip - name: Install Dependencies run: composer install --no-dev --optimize-autoloader --no-interaction - name: Run Tests run: ./vendor/bin/phpunit - name: Deploy to Server uses: appleboy/ssh-action@master with: host: ${{ secrets.SSH_HOST }} username: ${{ secrets.SSH_USERNAME }} key: ${{ secrets.SSH_PRIVATE_KEY }} port: 22 script: | cd /var/www/your-project git pull origin main composer install --no-dev --optimize-autoloader --no-interaction php artisan migrate --force # Laravel specific, run DB migrations php artisan cache:clear # Laravel specific, clear cache """ **Explanation:** * The YAML file details the CI/CD workflow triggered upon pushing to the "main" branch. * It sets up PHP 8.2 (latest stable version at the time of writing) with necessary extensions. * Install Composer dependencies and run PHPUnit tests to ensure code integrity. * Uses SSH to securely deploy the code to the production server, pulls the latest changes, installs dependencies and runs database migrations. **Anti-Pattern:** Configuring CI/CD pipelines directly on production servers. Pipelines should exist as code within your repository. ### 1.2. Build Artifacts * **Do This:** Create build artifacts (e.g., tarballs, Docker images) for deployments. * **Don't Do This:** Deploy directly from the repository without creating intermediate artifacts. **Explanation:** Build artifacts ensure consistency across different environments and are especially useful during rollbacks. They encapsulate a specific, tested version of the application. **Creating a Build Artifact (Tarball):** """bash #!/bin/bash VERSION=$(git describe --tags --abbrev=0) TIMESTAMP=$(date +%Y%m%d%H%M%S) ARTIFACT_NAME="your-project-${VERSION}-${TIMESTAMP}.tar.gz" # Exclude development-related directories tar -czvf "$ARTIFACT_NAME" \ --exclude='./.git' \ --exclude='./node_modules' \ --exclude='./tests' \ --exclude='./.github' \ . echo "Created artifact: $ARTIFACT_NAME" # Optionally, upload to an artifact storage (e.g., AWS S3, Artifactory) # aws s3 cp "$ARTIFACT_NAME" s3://your-bucket/artifacts/ """ **Explanation:** This script creates a compressed tarball of your project directory, excluding development-related files (like ".git", "node_modules", and test directories). The artifact name includes the version and timestamp for easy identification. **Using Docker for Artifacts:** """dockerfile # Dockerfile FROM php:8.2-fpm-alpine # Use a specific PHP 8.2 image WORKDIR /var/www/html COPY . /var/www/html RUN apk add --no-cache $PHPIZE_DEPS \ && pecl install xdebug \ && docker-php-ext-enable xdebug \ && apk del $PHPIZE_DEPS \ && composer install --no-dev --optimize-autoloader --no-interaction EXPOSE 9000 CMD ["php-fpm"] """ **Explanation:** * Uses a PHP 8.2 FPM Alpine image as a base. * Copies the application code, installs dependencies, and configures the environment. * The resulting Docker image serves as a complete, reproducible build artifact. **Anti-Pattern:** Including sensitive information (API keys, database passwords) directly inside a Docker image. Use environment variables or secrets management. ### 1.3. Version Control * **Do This:** Use a branching strategy (e.g., Gitflow) for managing releases, hotfixes, and feature development. * **Don't Do This:** Directly commit to the "main" branch, especially in collaborative or production-bound environments. **Explanation:** Branching allows parallel development efforts without stability issues and ensures a clear separation between stable and experimental code. **Example Gitflow Workflow:** 1. "develop" branch: Main development branch for feature integration. 2. "feature/*" branches: Created for new features based on the "develop" branch. Merge back into "develop". 3. "release/*" branches: Created for preparing a release from "develop". Merge into "main" and "develop". 4. "hotfix/*" branches: Created from "main" for critical bug fixes. Merge into "main" and "develop". 5. "main" branch: Represents production-ready code. Tagged with release versions. ## 2. Production Considerations ### 2.1. Environment Variables * **Do This:** Store all configurations (database credentials, API keys, external service endpoints) as environment variables. * **Don't Do This:** Hardcode sensitive data in code or configuration files directly. **Explanation:** Environment variables provide a standardized and secure way to configure applications without modifying the codebase. They are runtime-specific, allowing different configurations for development, staging, and production environments. **Using Environment Variables in PHP (with ".env" file and "vlucas/phpdotenv"):** """php <?php require_once __DIR__ . '/vendor/autoload.php'; use Dotenv\Dotenv; $dotenv = Dotenv::createImmutable(__DIR__); $dotenv->load(); $db_host = $_ENV['DB_HOST']; $db_user = $_ENV['DB_USER']; $db_pass = $_ENV['DB_PASS']; $db_name = $_ENV['DB_NAME']; echo "Connecting to database: $db_name on $db_host\n"; try { $pdo = new PDO("mysql:host=$db_host;dbname=$db_name", $db_user, $db_pass); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); echo "Successfully connected to the database.\n"; // Your database operations here... } catch (PDOException $e) { echo "Connection failed: " . $e->getMessage() . "\n"; } """ ".env" file (example): """ DB_HOST=localhost DB_USER=your_username DB_PASS=your_password DB_NAME=your_database API_KEY=your_secret_api_key """ **Explanation:** * Uses the "vlucas/phpdotenv" library to load environment variables from a ".env" file into the "$_ENV" superglobal. * Retrieves database credentials from environment variables. * This approach keeps credentials separate from the codebase and allows for different configurations in each environment. On real servers, environment variables should be set directly in the system environment, NOT relied upon from .env files. **Anti-Pattern:** Checking ".env" files into version control. This exposes secrets to anyone with access to the repository. ### 2.2. Logging and Monitoring * **Do This:** Implement comprehensive logging and monitoring solutions to proactively identify and address issues. * **Don't Do This:** Rely solely on server logs; use aggregated logging services and monitoring tools. **Explanation:** Logging provides insights into application behavior, while monitoring tracks performance metrics. They are crucial for identifying bottlenecks, errors, and security issues. **Example utilizing Monolog for Logging:** """php <?php require_once __DIR__ . '/vendor/autoload.php'; use Monolog\Logger; use Monolog\Handler\StreamHandler; // Create a log channel $log = new Logger('app'); $log->pushHandler(new StreamHandler(__DIR__.'/logs/app.log', Logger::WARNING)); // Log some events $log->warning('Foo'); $log->error('Bar'); """ **Explanation:** * Install Monolog: "composer require monolog/monolog" * Creates a Logger instance and configures it to write to a file ("app.log") with a minimum log level of "WARNING". * Logs a warning and an error message. Monolog offers fine-grained control over log format, handlers (file, database, syslog, etc..) and processors (adding extra context to log messages). **Monitoring with Prometheus and Grafana:** 1. **Expose Metrics (Example using a custom collector):** You'd need to install a Prometheus client library for PHP, such as "promphp/prometheus_client_php". This example uses a static variable to avoid having to build a proper, and long, class. """php <?php use Prometheus\CollectorRegistry; use Prometheus\Counter; class MyAppMetrics { private static ?Counter $httpRequestsTotalCounter = null; public static function getHttpRequestCounter(CollectorRegistry $registry): Counter { if (self::$httpRequestsTotalCounter === null) { self::$httpRequestsTotalCounter = $registry->registerCounter( 'http_requests_total', 'Total number of HTTP requests', ['method', 'path'] ); } return self::$httpRequestsTotalCounter; } } //Usage (In your request handling logic for example): require 'vendor/autoload.php'; use Prometheus\CollectorRegistry; use Prometheus\Storage\InMemory; $adapter = new InMemory(); $registry = new CollectorRegistry($adapter, false); $requestMethod = $_SERVER['REQUEST_METHOD']; $requestPath = $_SERVER['REQUEST_URI']; MyAppMetrics::getHttpRequestCounter($registry)->inc(['method' => $requestMethod, 'path' => $requestPath]); header('Content-type: text/plain'); $result = $registry->getMetricFamilySamples(); $serializer = new \Prometheus\RenderTextFormat(); $metricOutput = $serializer->render($result); echo $metricOutput; """ 2. **Configure Prometheus:** Configure Prometheus to scrape the exposed endpoint. 3. **Create Grafana Dashboard:** Visualize the data in Grafana. **Anti-Pattern:** Writing logs to a single file without rotation. This can lead to disk space exhaustion and performance degradation. Implement log rotation using tools like "logrotate". ### 2.3. Caching * **Do This:** Implement caching strategies at multiple levels (HTTP cache, opcode cache, data cache) to improve performance. * **Don't Do This:** Over-cache data or neglect cache invalidation strategies. **Explanation:** Caching reduces the load on your servers and databases by storing frequently accessed data in memory or on disk. Proper cache invalidation is crucial to avoid serving stale data. **Opcode Caching (Zend OPcache):** Ensure Zend OPcache is enabled and properly configured in "php.ini": """ini opcache.enable=1 opcache.enable_cli=1 opcache.memory_consumption=128 ; Adjust as needed opcache.interned_strings_buffer=8 ; Adjust as needed opcache.max_accelerated_files=10000 ; Adjust as needed opcache.validate_timestamps=1 ; Set to 0 in production (after thorough testing) """ **Explanation:** OPcache improves the performance of PHP by storing precompiled script bytecode in shared memory, thereby removing the need for PHP to load and parse scripts on each request. **HTTP Caching (Proper Headers):** """php <?php // Example: Cache for 1 hour $cache_expire = 60*60; header("Cache-Control: public, max-age=$cache_expire"); header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $cache_expire) . ' GMT'); ?> """ **Data Caching (Redis):** """php <?php require 'vendor/autoload.php'; use Predis\Client; $redis = new Client([ 'scheme' => 'tcp', 'host' => '127.0.0.1', 'port' => 6379, ]); $key = 'my_data'; $cachedData = $redis->get($key); if ($cachedData) { echo "Data from cache: " . $cachedData . "\n"; } else { $data = 'Some expensive data to compute'; // Simulate retrieving data from a database or other source $redis->set($key, $data); $redis->expire($key, 3600); // Cache for 1 hour echo "Data from source: " . $data . "\n"; } """ **Explanation:** * Installs redis via composer. * Connects to Redis. * Retrieves data from cache (if it exists). * If the data is not in the cache, it is retrieved from the source, stored in Redis, and a TTL (Time-To-Live) is set. This code shows the basic principle. More sophisticated caching solutions will track dependencies and cache keys for more accurate invalidation. **Anti-Pattern:** Using file-based caching in a distributed environment. This creates inconsistencies between servers. Use a centralized caching solution like Redis or Memcached. ### 2.4. Database Migrations * **Do This:** Use a database migration tool (e.g., Flyway, Doctrine Migrations, Laravel Migrations) to manage database schema changes. * **Don't Do This:** Apply database changes manually on production, or directly without version control. **Explanation:** Database migrations provide a version-controlled and automated way to evolve your database schema. This ensures consistency and allows you to easily roll back changes if necessary. **Laravel Migration Example:** """php <?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateUsersTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('users', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('email')->unique(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->rememberToken(); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('users'); } } """ **Explanation:** * Defines a migration class that creates a "users" table. * The "up()" method defines the schema changes to be applied. * The "down()" method defines the rollback operation (dropping the table). **Anti-Pattern:** Granting direct access to the production database to developers. Use migration tools and enforce code review processes for schema changes. ### 2.5. Security * **Do This:** Implement security best practices for PHP, including input validation, output encoding, protection against common web vulnerabilities (SQL injection, XSS, CSRF), and regular security audits. * **Don't Do This:** Trust user input directly, or expose detailed error messages in production. **Explanation:** Securing PHP applications requires a multi-layered approach, from code-level hardening to server-side protection. **Input Validation and Sanitization:** """php <?php $email = $_POST['email'] ?? ''; // Validate email format if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { echo "Invalid email format"; exit; } // Sanitize email to prevent injection $email = filter_var($email, FILTER_SANITIZE_EMAIL); """ **Output Encoding (preventing XSS):** """php <?php $username = $_POST['username'] ?? ''; // Escape output using htmlspecialchars echo htmlspecialchars($username, ENT_QUOTES, 'UTF-8'); ?> """ **Using Prepared Statements (preventing SQL injection):** """php <?php $stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?"); $stmt->execute([$email]); $user = $stmt->fetch(); ?> """ **Best practices checklist** * Keep PHP and all dependencies up to date with security patches. * Use a Content Security Policy (CSP) to mitigate XSS. * Enforce HTTPS and use HSTS. * Regularly scan your application for vulnerabilities using tools like Snyk or OWASP ZAP. ## 3. Modern Approaches and Patterns ### 3.1. Infrastructure as Code (IaC) * **Do This:** Define and manage infrastructure using code (e.g., Terraform, Ansible, CloudFormation). * **Don't Do This:** Manually configure servers and infrastructure components. **Explanation:** Infrastructure as Code allows you to provision and manage your infrastructure in a predictable, repeatable, and version-controlled manner. **Example Terraform Configuration (provisioning a web server):** """terraform resource "aws_instance" "web_server" { ami = "ami-0c55b243c1244368a" # Example AMI ID instance_type = "t2.micro" key_name = "your_key_pair" tags = { Name = "Web Server" } user_data = <<-EOF #!/bin/bash sudo apt update sudo apt install -y apache2 php libapache2-mod-php echo "<?php phpinfo(); ?>" | sudo tee /var/www/html/index.php sudo systemctl restart apache2 EOF } output "public_ip" { value = aws_instance.web_server.public_ip } """ **Explanation:** * This Terraform configuration defines a resource for an AWS EC2 instance, specifying the AMI, instance type, key pair, and tags. * The "user_data" script installs Apache, PHP, and a basic "phpinfo()" page on the instance. * Run "terraform init", "terraform plan", and "terraform apply" to provision the infrastructure. **Anti-pattern:** Storing Terraform state files locally. Use remote state storage (e.g., AWS S3, HashiCorp Consul) for collaboration and version control. ### 3.2. Containerization (Docker) and Orchestration (Kubernetes) * **Do This:** Package PHP applications into Docker containers for portability and scalability. Use Kubernetes or similar orchestration platforms for deployment and management. * **Don't Do This:** Deploy directly to virtual machines without containerization. **Explanation:** Containerization provides a consistent and isolated environment for running PHP applications. Kubernetes automates the deployment, scaling, and management of containerized applications. **Example Kubernetes Deployment:** """yaml apiVersion: apps/v1 kind: Deployment metadata: name: your-app-deployment spec: replicas: 3 selector: matchLabels: app: your-app template: metadata: labels: app: your-app spec: containers: - name: your-app-container image: your-docker-registry/your-app:latest ports: - containerPort: 80 env: - name: DB_HOST valueFrom: secretKeyRef: name: db-credentials key: host - name: DB_USER valueFrom: secretKeyRef: name: db-credentials key: user """ **Explanation:** * Defines a Kubernetes Deployment that manages a specified number of replicas of your application. * Specifies the Docker image to use, the container port, and environment variables. **Anti-Pattern:** Running a single container instance on a single server. Utilize Kubernetes or similar to benefit from auto-scaling, self-healing, and rolling deployments. ### 3.3. Microservices * **Do This:** Consider breaking down monolithic PHP applications into smaller, independent microservices. * **Don't Do This:** Unnecessarily create microservices for simple applications. **Explanation:** Microservices improve scalability, maintainability, and fault isolation. **Technology considerations when using microservices:** * Use an API gateway to route requests to the appropriate microservice. * Implement service discovery to allow microservices to locate each other. * Use asynchronous communication (e.g., message queues) for decoupling services. * Implement distributed tracing to monitor requests that span multiple microservices. --- By following these standards, PHP development teams can ensure their deployment and DevOps processes are reliable, automated, secure, and scalable. This will lead to faster release cycles, fewer production issues, and improved overall application quality.
# Core Architecture Standards for PHP This document outlines the core architectural standards for PHP development, focusing on fundamental patterns, project structure, and organization. Following these standards will improve maintainability, performance, security, and collaboration within development teams. This guide is tailored for modern PHP (version 8.1 and above), emphasizing current best practices. ## 1. Architectural Patterns ### 1.1. Layered Architecture **Standard:** Implement a layered architecture to separate concerns and improve maintainability. * **Do This:** Divide your application into distinct layers: Presentation (UI), Application (Business Logic), Domain (Core Concepts), and Infrastructure (Data Access, external services). * **Don't Do This:** Mix presentation logic directly with data access or business logic. **Why:** Layered architecture promotes separation of concerns, making code easier to understand, test, and modify. **Code Example:** """php // Infrastructure Layer (Data Access) namespace App\Infrastructure\Persistence\Doctrine; use App\Domain\Model\User; use App\Domain\Repository\UserRepositoryInterface; use Doctrine\ORM\EntityManagerInterface; class DoctrineUserRepository implements UserRepositoryInterface { private EntityManagerInterface $entityManager; public function __construct(EntityManagerInterface $entityManager) { $this->entityManager = $entityManager; } public function findById(int $id): ?User { return $this->entityManager->find(User::class, $id); } public function save(User $user): void { $this->entityManager->persist($user); $this->entityManager->flush(); } } // Domain Layer (Business Logic) namespace App\Domain\Model; class User { private int $id; private string $email; private string $password; private bool $isActive; public function __construct(string $email, string $password) { $this->email = $email; $this->password = password_hash($password, PASSWORD_DEFAULT); $this->isActive = true; } // Getters and setters (omitted for brevity) } // Application Layer (Use Cases) namespace App\Application\User; use App\Domain\Model\User; use App\Domain\Repository\UserRepositoryInterface; class RegisterUser { private UserRepositoryInterface $userRepository; public function __construct(UserRepositoryInterface $userRepository) { $this->userRepository = $userRepository; } public function execute(string $email, string $password): User { $user = new User($email, $password); $this->userRepository->save($user); return $user; } } // Presentation Layer (Controller) namespace App\Presentation\Http\Controller; use App\Application\User\RegisterUser; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; class UserController { #[Route('/register', methods: ['POST'])] public function register(Request $request, RegisterUser $registerUser): Response { $email = $request->request->get('email'); $password = $request->request->get('password'); $user = $registerUser->execute($email, $password); return new Response('User registered successfully with ID: ' . $user->getId(), Response::HTTP_CREATED); } } """ **Anti-Pattern:** A monolithic class that handles everything from request processing to database interaction. ### 1.2. Domain-Driven Design (DDD) **Standard:** Consider DDD principles for complex business domains. * **Do This:** Model your domain using Entities, Value Objects, Aggregates, and Services. Clearly define a Ubiquitous Language. * **Don't Do This:** Treat your domain model as simple data transfer objects (DTOs). Avoid anemic domain models. **Why:** DDD helps create a robust and maintainable system that accurately reflects the business domain. **Code Example:** """php // Value Object namespace App\Domain\ValueObject; use InvalidArgumentException; class Email { private string $email; public function __construct(string $email) { if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { throw new InvalidArgumentException('Invalid email format'); } $this->email = $email; } public function __toString(): string { return $this->email; } public function equals(Email $other): bool { return $this->email === $other->email; } } // Entity namespace App\Domain\Model; use App\Domain\ValueObject\Email; class Customer { private int $id; private Email $email; private string $name; public function __construct(Email $email, string $name) { $this->email = $email; $this->name = $name; } public function getEmail(): Email { return $this->email; } // Other methods } // Aggregate Root namespace App\Domain\Model; class Order { private int $id; private Customer $customer; private array $lineItems = []; public function __construct(Customer $customer) { $this->customer = $customer; } public function addLineItem(LineItem $lineItem): void { $this->lineItems[] = $lineItem; } public function getTotalAmount(): float { return array_sum(array_map(fn(LineItem $item) => $item->getPrice() * $item->getQuantity(), $this->lineItems)); } //Other methods } // Domain Service namespace App\Domain\Service; use App\Domain\Model\Order; class DiscountService { public function applyCustomerDiscount(Order $order): float { //Complex discount logic based on customer history. return $order->getTotalAmount() * 0.9; // 10% discount. } } """ **Anti-Pattern:** Building a system focused on database tables rather than business concepts. Ignoring the ubiquitous language. ### 1.3. Microservices Architecture **Standard:** Adopt microservices when appropriate for large, complex applications requiring independent deployment and scalability. * **Do This:** Design services around specific business capabilities. Use lightweight communication protocols like HTTP/REST or messaging queues. * **Don't Do This:** Create tightly coupled services that depend on each other's internal implementation details. Build a distributed monolith. **Why:** Microservices allow teams to work independently, scale specific parts of the application as needed, and use different technologies where appropriate. **Code Example (simplified, illustrating HTTP communication):** """php // Service A (Product Service) namespace App\ProductService; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Annotation\Route; class ProductController { #[Route('/products/{id}', methods: ['GET'])] public function getProduct(int $id): JsonResponse { // Retrieve product information $product = ['id' => $id, 'name' => 'Example Product']; return new JsonResponse($product); } } // Service B (Order Service) namespace App\OrderService; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Symfony\Contracts\HttpClient\HttpClientInterface; class OrderController { private HttpClientInterface $httpClient; public function __construct(HttpClientInterface $httpClient) { $this->httpClient = $httpClient; } #[Route('/orders', methods: ['POST'])] public function createOrder(Request $request): Response { $productId = $request->request->get('productId'); // Call Product Service to get product details $response = $this->httpClient->request( 'GET', 'http://product-service/products/' . $productId ); $product = $response->toArray(); // Process order creation using the product details // ... return new Response('Order created successfully for product: ' . $product['name']); } } """ **Anti-Pattern:** Breaking down a small, simple application into microservices prematurely (over-engineering). Shared database schema across multiple microservices. ## 2. Project Structure and Organization ### 2.1. Namespace Usage **Standard:** Utilize namespaces consistently to organize code and prevent naming collisions. * **Do This:** Follow the PSR-4 autoloading standard. Use meaningful namespaces that reflect the project's structure and domain. * **Don't Do This:** Use global namespace for your application code. **Why:** Namespaces improve code organization, prevent naming conflicts, and make it easier to autoload classes. **Code Example:** """php // File: src/MyProject/Domain/Model/User.php namespace MyProject\Domain\Model; class User { // ... } // File: src/MyProject/Infrastructure/Persistence/UserRepository.php namespace MyProject\Infrastructure\Persistence; use MyProject\Domain\Model\User; class UserRepository { public function find(int $id): ?User { // ... } } """ **Anti-Pattern:** Inconsistent namespace usage across the project. Very long and unwieldy namespace names. ### 2.2. Directory Structure **Standard:** Employ a consistent and logical directory structure. * **Do This:** Separate code by layers or modules (e.g., "src/Domain", "src/Application", "src/Infrastructure", "src/Presentation"). Consider a "config" directory for configuration files, and "tests" for tests. * **Don't Do This:** Dump all PHP files into a single directory. **Why:** A clear directory structure makes it easier to navigate the codebase and locate specific files. **Code Example (example structure):** """ my-project/ ├── config/ # Configuration files ├── src/ # Source code │ ├── Domain/ # Domain layer (Entities, Value Objects, Interfaces) │ ├── Application/ # Application layer (Use Cases, Services) │ ├── Infrastructure/ # Infrastructure layer (Data Access, External Integrations) │ ├── Presentation/ # Presentation layer (Controllers, Views) ├── tests/ # Unit and Integration tests ├── composer.json ├── composer.lock """ **Anti-Pattern:** Randomly placing files without a clear organizational scheme. Mixing configuration files with source code. ### 2.3. Dependency Injection (DI) and Inversion of Control (IoC) **Standard:** Utilize Dependency Injection and Inversion of Control to decouple components. * **Do This:** Use constructor injection to provide dependencies. Consider using a DI container (e.g., Symfony's DI container, PHP-DI, or similar) for managing dependencies. Define interfaces for dependencies. * **Don't Do This:** Create tight coupling by directly instantiating dependencies within classes. Use static factories excessively. **Why:** DI promotes loose coupling, making code more testable, maintainable, and reusable. IoC allows for dynamic configuration and swapping of implementations. **Code Example:** """php // Interface namespace App\Domain\Repository; interface LoggerInterface { public function log(string $message): void; } // Implementation namespace App\Infrastructure\Logger; class FileLogger implements LoggerInterface { private string $logFile; public function __construct(string $logFile) { $this->logFile = $logFile; } public function log(string $message): void { file_put_contents($this->logFile, $message . "\n", FILE_APPEND); } } // Class utilizing Dependency Injection namespace App\Application\Service; use App\Domain\Repository\LoggerInterface; class UserService { private LoggerInterface $logger; public function __construct(LoggerInterface $logger) { $this->logger = $logger; } public function createUser(string $username, string $email): void { // ... create user logic ... $this->logger->log("User created: " . $username); } } // Configuration (using Symfony's DI container as example) use App\Application\Service\UserService; use App\Infrastructure\Logger\FileLogger; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; $containerBuilder = new ContainerBuilder(); $containerBuilder->register('logger', FileLogger::class) ->addArgument('%kernel.logs_dir%/app.log'); $containerBuilder->register('user_service', UserService::class) ->addArgument(new Reference('logger')); $containerBuilder->compile(); /** @var UserService $userService */ $userService = $containerBuilder->get('user_service'); $userService->createUser("testuser", "test@example.com"); """ **Anti-Pattern:** Creating service locators instead of using constructor injection. Hardcoding dependencies. ### 2.4. Configuration Management **Standard:** Centralize configuration using environment variables, configuration files, or dedicated configuration management tools. * **Do This:** Use environment variables for sensitive information (API keys, database passwords). Store application settings in configuration files (e.g., YAML, JSON, INI). Utilize libraries like "Symfony\Component\Config" for loading and managing configuration. Consider using tools like Dotenv to manage environment variables in development. * **Don't Do This:** Hardcode configuration values directly in the code. Commit sensitive information to the repository. **Why:** Proper configuration management makes it easier to deploy the application to different environments and avoids exposing sensitive data. **Code Example:** """php // .env (example using Dotenv) DATABASE_URL=mysql://user:password@host:port/database API_KEY=YOUR_API_KEY // Using environment variables $databaseUrl = $_ENV['DATABASE_URL']; $apiKey = $_ENV['API_KEY']; //Using symfony config component for YAML configuration (config/services.yaml): #Example content for config/services.yaml inside Symfony project services: App\Service\MyService: arguments: $apiKey: '%env(API_KEY)%' // In the MyService class: namespace App\Service; class MyService { private string $apiKey; public function __construct(string $apiKey) { $this->apiKey = $apiKey; } public function doSomething(): void { // Use the API key } } """ **Anti-Pattern:** Scattering configuration values throughout the code. Storing sensitive data in plain text in configuration files. ## 3. Implementation Details & Best Practices ### 3.1. Error Handling and Logging **Standard:** Implement robust error handling and logging. * **Do This:** Use exceptions for exceptional situations. Implement a global exception handler. Log errors, warnings, and important events using a logging library (e.g., Monolog). Use structured logging formats (e.g., JSON) for easier analysis. Include context information in log messages (e.g., user ID, request ID). * **Don't Do This:** Suppress errors without logging them. "die()" or "exit()" in production code. Expose sensitive information in error messages. **Why:** Proper error handling prevents application crashes and makes it easier to diagnose issues. Logging provides valuable insights into the application's behavior. **Code Example:** """php use Monolog\Logger; use Monolog\Handler\StreamHandler; use Exception; // Create a log channel $log = new Logger('my_app'); $log->pushHandler(new StreamHandler(__DIR__.'/app.log', Logger::WARNING)); try { // Some code that might throw an exception $result = 10 / 0; // Division by zero } catch (Exception $e) { // Log the error $log->error('An error occurred: ' . $e->getMessage(), ['exception' => $e]); // Display a user-friendly error message (don't expose sensitive details) echo "An unexpected error occurred. Please try again later."; } //Global exception handling in Symfony (example) // config/services.yaml services: App\EventListener\ExceptionListener: class: App\EventListener\ExceptionListener arguments: ['%kernel.debug%'] # Pass kernel.debug to check if in dev environment. tags: - { name: kernel.event_listener, event: kernel.exception, method: onKernelException } // src/EventListener/ExceptionListener.php namespace App\EventListener; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpKernel\Event\ExceptionEvent; use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; use Psr\Log\LoggerInterface; class ExceptionListener { private bool $debug; private LoggerInterface $logger; public function __construct(bool $debug, LoggerInterface $logger) { $this->debug = $debug; $this->logger = $logger; } public function onKernelException(ExceptionEvent $event): void { $exception = $event->getThrowable(); $this->logger->error($exception->getMessage(), ['exception' => $exception]); $response = new JsonResponse(); if ($this->debug) { $response->setData([ 'message' => $exception->getMessage(), 'trace' => $exception->getTrace(), ]); } else { $statusCode = $exception instanceof HttpExceptionInterface ? $exception->getStatusCode() : 500; $response->setStatusCode($statusCode); $response->setData(['message' => 'An error occurred. Please try again later.']); } $event->setResponse($response); } } """ **Anti-Pattern:** Using "@" to silence errors without proper handling. Catching general "Exception" without specific handling. Not using a logging library. ### 3.2. Code Style and Formatting **Standard:** Adhere to a consistent code style. * **Do This:** Follow the PSR-12 coding style guide ([https://www.php-fig.org/psr/psr-12/](https://www.php-fig.org/psr/psr-12/)). Use a code formatter (e.g., PHP-CS-Fixer) and a linter (e.g., PHPStan, Psalm) to automatically enforce the coding style. * **Don't Do This:** Use inconsistent indentation, spacing, or naming conventions. **Why:** Consistent code style improves readability and maintainability. **Code Example (demonstrating PSR-12):** """php <?php namespace MyProject\Example; use DateTimeInterface; class Example { public function myFunction(int $param1, string $param2): void { if ($param1 > 0) { echo "Param1 is positive"; } else { echo "Param1 is not positive"; } } public function anotherFunction(DateTimeInterface $date): string { return $date->format('Y-m-d H:i:s'); } } """ **Anti-Pattern:** Inconsistent indentation. Mixing tabs and spaces. Overly long lines of code. Ignoring code style guidelines. ### 3.3. Performance Optimization **Standard:** Write efficient code and optimize for performance. * **Do This:** Use appropriate data structures and algorithms. Minimize database queries. Use caching (e.g., Redis, Memcached) for frequently accessed data. Profile your code to identify performance bottlenecks. Use opcache for bytecode caching. Lazy load data and resources when appropriate. Avoid N+1 query problems. * **Don't Do This:** Perform unnecessary computations. Load large datasets into memory unnecessarily. Ignore performance testing. **Why:** Performance optimization ensures that the application responds quickly and efficiently, providing a good user experience and reducing server costs. **Code Example:** """php // Caching using Redis $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $cacheKey = 'user_data_' . $userId; $userData = $redis->get($cacheKey); if ($userData === false) { // Data not in cache, fetch from database $userData = $this->fetchUserDataFromDatabase($userId); $redis->set($cacheKey, serialize($userData), 3600); // Cache for 1 hour } else { $userData = unserialize($userData); } // Avoiding N+1 query problem using Doctrine (eager loading) // In your repository: /** * @return Collection<int, Product> */ public function findAll(): array { return $this->createQueryBuilder('p') ->leftJoin('p.category', 'c') //Eager loading of category ->addSelect('c') ->orderBy('p.id', 'ASC') ->getQuery() ->getResult() ; } """ **Anti-Pattern:** Ignoring database indexes. Retrieving all columns from a table when only a few are needed. Not using profiling tools. ### 3.4. Security Best Practices **Standard:** Implement security best practices to protect against common vulnerabilities. * **Do This:** Sanitize and validate all user inputs. Use prepared statements to prevent SQL injection. Escape output to prevent XSS attacks. Implement proper authentication and authorization. Use strong passwords and hashing algorithms. Protect against CSRF attacks. Keep your dependencies up to date. Utilize static analysis tools to identify potential security flaws. * **Don't Do This:** Trust user input without validation. Store passwords in plain text. Disable security features. Expose sensitive information in error messages. **Why:** Security best practices protect the application and its users from malicious attacks. **Code Example:** """php // Prepared statement to prevent SQL injection $statement = $pdo->prepare("SELECT * FROM users WHERE email = :email"); $statement->bindParam(':email', $email, PDO::PARAM_STR); $statement->execute(); // Output escaping to prevent XSS $escapedName = htmlspecialchars($userName, ENT_QUOTES, 'UTF-8'); echo "Hello, " . $escapedName; //Password Hashing: $password = 'P@$$wOrd'; $hashedPassword = password_hash($password, PASSWORD_DEFAULT); // Verification if (password_verify($password, $hashedPassword)) { echo 'Password is valid!'; } else { echo 'Invalid password.'; } """ **Anti-Pattern:** Using "eval()". Storing API keys in client-side code. Not validating file uploads. ### 3.5. Testing **Standard:** Write automated tests for all critical parts of the application. * **Do This:** Write unit tests, integration tests, and end-to-end tests. Use a testing framework (e.g., PHPUnit, Pest). Aim for high test coverage. Use mocks and stubs to isolate units of code. Follow the Arrange-Act-Assert pattern in your tests. * **Don't Do This:** Skip testing. Write tests that are brittle and difficult to maintain. Test implementation details instead of behavior. **Why:** Testing ensures that the application works as expected and prevents regressions. **Code Example:** """php // Unit test using PHPUnit use PHPUnit\Framework\TestCase; use App\Domain\Model\User; class UserTest extends TestCase { public function testCreateUser(): void { $user = new User('test@example.com', 'password'); $this->assertInstanceOf(User::class, $user); // $this->assertEquals('test@example.com', $user->getEmail()); //Assuming you have a getEmail() method. } } """ **Anti-Pattern:** Writing integration tests that rely on external services without mocking. Testing private methods directly. Not running tests regularly. ### 3.6 API Design **Standard:** Design APIs that are well-documented, secure and follow RESTful principles where appropriate. * **Do This:** Use standard HTTP methods (GET, POST, PUT, DELETE). Use meaningful endpoint names ("/users", "/products/{id}"). Return appropriate HTTP status codes. Use a standard data format (e.g., JSON). Use API versioning. Document your API using tools like OpenAPI (Swagger). Implement rate limiting to prevent abuse. Authenticate and authorize API requests. * **Don't Do This:** Violate RESTful principles without a good reason. Expose internal implementation details in the API. Neglect API documentation. **Why:** Well-designed APIs are easy to use, maintain, and extend. **Code Example:** """php // A simple RESTful API endpoint (example using Symfony) namespace App\Controller; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; class ApiProductController { #[Route('/api/products/{id}', methods: ['GET'])] public function getProduct(int $id): JsonResponse { $product = ['id' => $id, 'name' => 'Example Product', 'price' => 19.99]; if (!$product) { return new JsonResponse(['message' => 'Product not found'], Response::HTTP_NOT_FOUND); } return new JsonResponse($product); } } // OpenAPI (Swagger) documentation (example snippet) /** * @OA\Get( * path="/api/products/{id}", * summary="Get a product by ID", * @OA\Parameter( * name="id", * in="path", * description="Product ID", * required=true, * @OA\Schema(type="integer") * ), * @OA\Response( * response="200", * description="Successful operation", * @OA\JsonContent( * type="object", * @OA\Property(property="id", type="integer"), * @OA\Property(property="name", type="string"), * @OA\Property(property="price", type="number", format="float") * ) * ), * @OA\Response( * response="404", * description="Product not found" * ) * ) */ """ **Anti-Pattern:** Designing APIs that are difficult to understand and use. Not providing proper error handling. Exposing sensitive data through the API.
# State Management Standards for PHP This document outlines the standards for managing application state in PHP, encompassing data flow, reactivity, and persistence. These standards aim to promote maintainable, performant, and secure applications. PHP has traditionally been stateless, but modern frameworks and design patterns enable effective state management. ## 1. Architecture and General Principles ### 1.1. Stateless vs. Stateful * **Do This:** Embrace stateless principles where possible, especially for API endpoints. Each request should contain all the necessary information for processing. * **Don't Do This:** Rely on server-side session state for everything. Over-reliance on sessions can lead to scalability issues. * **Why:** Statelessness simplifies scaling, reduces server resource consumption, and enhances predictability. For stateful interactions, consider well-defined, minimized state management mechanisms. ### 1.2. Explicit State Management * **Do This:** Clearly define where state is managed (client-side, server-side, database), its lifespan, and how it’s accessed and modified. * **Don't Do This:** Implicitly rely on global variables or hidden state that make the code hard to reason about. * **Why:** Explicit state management improves code readability, debuggability, and maintainability. It also allows for easier auditing and security analysis. ### 1.3. Separation of Concerns * **Do This:** Separate state management logic from business logic. Use dedicated classes or services to handle state operations. * **Don't Do This:** Mix state manipulation directly into controllers or other components responsible for handling user requests. * **Why:** Separation of concerns keeps the codebase organized, testable, and reduces the risk of unintended side effects. ### 1.4. Immutable State * **Do This:** When appropriate, leverage immutable data structures. For example, when dealing with configuration or read-only data, use immutable classes. * **Don't Do This:** Mutate state directly without considering the consequences. Mutation can lead to unexpected bugs, especially in concurrent environments. * **Why:** Immutability simplifies reasoning about state and helps prevent accidental modifications. In PHP 8.1 and later, read-only properties provide a strong mechanism for enforcing immutability. ## 2. State Management Techniques ### 2.1. Sessions * **Do This:** Use PHP's built-in session management features judiciously for managing user authentication and authorization. Utilize "session_start()", "$_SESSION", "session_regenerate_id()", and "session_destroy()" appropriately. Configure session options such as "session.cookie_secure", "session.cookie_httponly", and "session.gc_maxlifetime" in "php.ini" or using "ini_set()" for security. * **Don't Do This:** Store sensitive data like passwords directly in sessions. Avoid storing large objects in sessions as this can impact performance. * **Why:** Sessions provide a convenient way to maintain user-specific state across multiple requests. Securely configuring session options is critical. """php <?php // Secure session configuration (put in a central configuration file) ini_set('session.cookie_secure', true); // Only send cookies over HTTPS ini_set('session.cookie_httponly', true); // Prevent JavaScript access to the cookie ini_set('session.gc_maxlifetime', 3600); // Session expires after 1 hour of inactivity session_start(); // Store user ID in the session after authentication $_SESSION['user_id'] = $user->getId(); // Regenerate session ID after successful login to prevent session fixation session_regenerate_id(true); // Accessing data if (isset($_SESSION['user_id'])) { $userId = $_SESSION['user_id']; // ... } // Destroy session on logout session_destroy(); // Clears the $_SESSION array and unsets session data session_start(); // Start a new session for the logout process so that we can display some message $_SESSION['message'] = "Logged out successfully"; header("Location: /login"); // redirects the user to the login page exit; // Ensure that no further code is executed ?> """ ### 2.2. Cookies * **Do This:** Use cookies sparingly for non-sensitive data, such as user preferences or tracking identifiers. Set appropriate expiration times and use the "SameSite" attribute for enhanced security. Always encode and sanitize cookie data. * **Don't Do This:** Store sensitive information in cookies, like authentication tokens or personal details. Trust cookie data without validation. * **Why:** Cookies are stored client-side and are susceptible to tampering. Proper handling is essential to prevent vulnerabilities. """php <?php // Setting a cookie with security attributes setcookie( 'user_preference', 'dark_mode', [ 'expires' => time() + (30 * 24 * 60 * 60), // Expires in 30 days 'path' => '/', 'domain' => $_SERVER['HTTP_HOST'], // restrict cookie to your domain 'secure' => true, // Only send over HTTPS 'httponly' => true, // Prevent JavaScript access 'samesite' => 'Lax', // Mitigate CSRF attacks ] ); // Accessing a cookie if (isset($_COOKIE['user_preference'])) { $preference = htmlspecialchars($_COOKIE['user_preference'], ENT_QUOTES, 'UTF-8'); // ...Use the preference safely after HTML encoding } // Deleting a cookie setcookie('user_preference', '', ['expires' => time() - 3600, 'path' => '/']); // set expiry to past time ?> """ ### 2.3. Database * **Do This:** Persist application state in a database when it needs to be persistent across sessions. Use parameterized queries or prepared statements to prevent SQL injection. Normalise your database schema. Consider using an ORM (Doctrine, Eloquent). * **Don't Do This:** Store unstructured data directly in the database (e.g., serializing large objects) unless absolutely necessary. Write raw SQL queries without proper escaping. * **Why:** The database is the primary persistent store for most applications. Secure and efficient database interactions are paramount. """php <?php use Doctrine\ORM\EntityManagerInterface; use App\Entity\User; class UserService { private EntityManagerInterface $entityManager; public function __construct(EntityManagerInterface $entityManager) { $this->entityManager = $entityManager; } public function updateUser(int $userId, array $data): User { $user = $this->entityManager->getRepository(User::class)->find($userId); if (!$user) { throw new \Exception("User not found"); } $user->setName($data['name'] ?? $user->getName()); $user->setEmail($data['email'] ?? $user->getEmail()); // Other potential updates based on $data array $this->entityManager->flush(); // persists the changes to the database return $user; } } // Example Usage $userService = new UserService($entityManager); $updatedUser = $userService->updateUser(123, ['name' => 'John Doe', 'email' => 'john.doe@example.com']); echo "Updated user: " . $updatedUser->getName(); ?> """ ### 2.4. Caching * **Do This:** Utilize caching mechanisms (Redis, Memcached, APCu) to store frequently accessed data or computed results to improve performance. Set appropriate cache expiration policies (TTL). Use cache tags/invalidation strategies. * **Don't Do This:** Cache sensitive information without encryption. Cache data indefinitely without any invalidation strategy. * **Why:** Caching can significantly reduce database load and improve response times. """php <?php use Symfony\Component\Cache\Adapter\RedisAdapter; // Connect to Redis $redis = new RedisAdapter( RedisAdapter::createConnection('redis://localhost') ); $cacheKey = 'user_profile_' . $userId; $userProfile = $redis->get($cacheKey, function () use ($userId, $db) { // Fetch user profile from the database (expensive operation) $userProfile = $db->fetchUserProfile($userId); return $userProfile; }); // Use the retrieved user profile echo $userProfile['name']; // Invalidate cache $redis->delete($cacheKey); """ ### 2.5. Client-Side State * **Do This:** If using JavaScript frameworks (React, Vue, Angular), manage UI state on the client-side whenever possible. Use appropriate state management libraries (Redux, Vuex, Pinia) for complex applications. Employ local storage or session storage for persistent client-side data. * **Don't Do This:** Expose sensitive server-side data to the client that isn't needed for the UI. Rely *solely* on client-side state for critical application functionality. * **Why:** Client-side state management can improve UI responsiveness and offload processing from the server. ### 2.6. Request Attributes * **Do This:** In frameworks like Symfony or Laravel, use request attributes to pass data between middleware and controllers within a single request. Utilize value objects where appropriate for type-hinting and validation. * **Don't Do This:** Rely on global variables or session state for data that is only needed within a single request. * **Why:** Request attributes provide a clean and scoped way to manage data flow within a request lifecycle. """php <?php namespace App\Middleware; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Message\ResponseInterface; class UserMiddleware { public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { $userId = $request->getHeaderLine('X-User-ID'); if ($userId) { // In a real application, validate the ID and fetch the user $user = ['id' => $userId, 'name' => 'Example User']; // Set the user data as a request attribute $request = $request->withAttribute('user', $user); } return $handler->handle($request); } } namespace App\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; class MyController { public function index(Request $request): Response { $user = $request->attributes->get('user'); if ($user) { return new Response('Hello, ' . $user['name']); } else { return new Response('Hello, Guest'); } } } ?> """ ## 3. Modern Approaches and Patterns ### 3.1. CQRS (Command Query Responsibility Segregation) * **Do This:** Consider applying CQRS for applications with complex data models and high read/write loads. Separate read and write operations into distinct models. * **Don't Do This:** Overcomplicate simple applications with CQRS. It's best suited for scenarios where the benefits outweigh the added complexity. * **Why:** CQRS allows you to optimize read and write models independently, improving performance and scalability. ### 3.2. Event Sourcing * **Do This:** Explore Event Sourcing for applications where you need a complete audit trail of all state changes. Store the sequence of events that led to the current state. * **Don't Do This:** Use Event Sourcing as a replacement for traditional CRUD operations without understanding its implications. It requires a different mindset and infrastructure. * **Why:** Event Sourcing provides a reliable and auditable history of state changes, enabling features like time-travel debugging and replayability. ### 3.3. Reactive Programming * **Do This:** Consider integrating reactive programming principles (e.g., using libraries like RxPHP) for handling asynchronous data streams and UI updates. * **Don't Do This:** Introduce reactive programming without thoroughly understanding its concepts (Observables, Subjects, Schedulers). It can add complexity if not used correctly. * **Why:** Reactive programming enables building responsive and event-driven applications that can handle large volumes of data efficiently. ### 3.4. State Machines * **Do This:** Model complex application workflows as state machines using libraries like "Finite" or "Symfony Workflow". Clearly define states, transitions, and associated actions. * **Don't Do This:** Implement complex state logic using nested "if" statements or procedural code. This leads to unmaintainable code. * **Why:** State machines provide a structured and visual way to manage application state transitions, reducing complexity and improving maintainability. ## 4. Security Considerations ### 4.1. Session Security * **Do This:** Use HTTPS to encrypt session cookies. Set "session.cookie_secure" and "session.cookie_httponly" directives. Regenerate session IDs after login and logout. Implement session timeouts. Store sensitive data securely (hashed passwords, etc.). Use "session_regenerate_id(true)" to delete the old session file. * **Don't Do This:** Store sensitive information directly in the session without encryption or proper sanitization. Use default session settings. Rely solely on session cookies for authentication without additional security mechanisms. * **Why:** Sessions are a prime target for attackers. Proper configuration and security measures are essential to prevent session hijacking and other vulnerabilities. ### 4.2. Cookie Security * **Do This:** Use the "secure" attribute for cookies containing sensitive data so they are only transmitted over HTTPS. Set the "httponly" attribute to prevent JavaScript access. Use the "SameSite" attribute to mitigate CSRF attacks. Validate cookie data to prevent tampering. * **Don't Do This:** Store sensitive data in cookies without encryption or proper encoding. * **Why:** Cookies are stored client-side and can be easily tampered with. Secure cookie configuration is critical to prevent vulnerabilities. ### 4.3. Database Security * **Do This:** Use parameterized queries or prepared statements to prevent SQL injection. Enforce the principle of least privilege for database users. Regularly update database software. Store sensitive data encrypted in the database. * **Don't Do This:** Construct SQL queries using string concatenation with user-supplied data. Grant excessive database privileges. * **Why:** SQL injection is a common and serious vulnerability. Proper database security practices are crucial to protect data integrity and confidentiality. ### 4.4. Caching Security * **Do This:** Avoid caching sensitive information without encryption. Implement proper access control to cache stores. Invalidate cache data when underlying data changes. Use a cache backend that supports authentication and authorization. * **Don't Do This:** Cache encrypted or sensitive data without encryption. * **Why:** Caches can be a source of information leakage if not properly secured. ## 5. Performance Optimization ### 5.1. Session Optimization * **Do This:** Minimize the amount of data stored in sessions. Use session handlers to store session data in a database or other persistent store instead of the default file-based storage, especially in clustered environments. Consider lazy-loading session data. * **Don't Do This:** Store large objects in sessions. Enable "session.auto_start" in production environments (it can lead to unnecessary overhead). * **Why:** Excessive session data can significantly impact performance and scalability. ### 5.2. Caching Optimization * **Do This:** Use appropriate cache expiration policies (TTL) based on the volatility of the data. Use cache invalidation strategies to ensure that cached data is up-to-date. Use hierarchical caching (e.g., combining APCu and Redis). Utilize compression to reduce the size of cached data. * **Don't Do This:** Cache data indefinitely without any invalidation strategy. Over-cache data, which can lead to stale content and inconsistent application behavior. * **Why:** Effective caching can dramatically reduce database load and improve response times. Improper caching can lead to various problems. ### 5.3. Database Optimization * **Do This:** Use database indexes to optimize query performance. Profile slow queries and optimize them. Use connection pooling to reduce database connection overhead. Use caching for frequently accessed data. * **Don't Do This:** Neglect database optimization. Write inefficient queries. * **Why:** Database performance is critical for application responsiveness. ## 6. Emerging Technology and Language Features ### 6.1 Readonly Classes and Properties * **Do This:** Utilize readonly properties and classes (PHP 8.1+) to define immutable state, ensuring data integrity and predictability. * **Don't Do This:** Modify readonly properties after object instantiation unless specifically allowed within the constructor. * **Why:** Readonly properties enforce immutability, which simplifies reasoning about state management and reduces the potential for accidental modification, enhancing code reliability. """php class Configuration { public readonly string $apiKey; public readonly int $timeout; public function __construct(string $apiKey, int $timeout) { $this->apiKey = $apiKey; $this->timeout = $timeout; } } $config = new Configuration('your_api_key', 30); // $config->timeout = 60; // This will cause Error: Cannot modify readonly property echo $config->apiKey; // Outputs 'your_api_key' """ ### 6.2 Fibre-Based Concurrency * **Do This:** Explore Fibres (PHP 8.1+) to manage concurrent state within a single request, useful for non-blocking I/O operations and asynchronous tasks. * **Don't Do This:** Assume Fibres replace multi-threading entirely; they're best suited for I/O-bound, not CPU-bound, operations. * **Why:** Fibres enable efficient context switching for asynchronous tasks, improving responsiveness and resource utilization, especially when dealing with external services. """php $fiber = new Fiber(function (): void { echo "About to suspend\n"; $value = Fiber::suspend('fiber-suspended'); echo "Value after suspending: " . $value . "\n"; }); echo "Starting fiber\n"; $fiber->start(); var_dump($fiber->getStatus()); //Outputs int(2) corresponding to Fiber::STATUS_SUSPENDED echo "Resuming fiber\n"; $fiber->resume('fiber-resumed'); //Pass a value to the suspended Fiber """ These standards provide clear guidelines for managing state effectively in PHP applications. By following these standards, developers can build more maintainable, performant, and secure applications. Implementing them into the IDE's linting and auto-completion system provides the kind of immediate feedback needed for high development velocity and high quality.
# Security Best Practices Standards for PHP This document outlines the security best practices for PHP development. It aims to guide developers in writing secure, maintainable, and performant code, while also providing context for AI coding assistants. These standards emphasize modern approaches and patterns, utilizing the latest PHP features to mitigate common vulnerabilities. ## 1. General Security Principles ### 1.1. Input Validation and Sanitization **Standard:** All user inputs must be validated and sanitized before being used in any operation. **Why:** Prevents injection attacks (SQL, XSS, command injection) and ensures data integrity. **Do This:** * Use "filter_var()" with appropriate filters for different input types (e.g., "FILTER_VALIDATE_EMAIL", "FILTER_SANITIZE_STRING"). * Use prepared statements with parameterized queries for database interactions. * Implement input validation on both the client-side (for UX) and server-side (for security). * Sanitize data based on its intended use, rather than blanket sanitization. **Don't Do This:** * Directly use user input in SQL queries without escaping or parameterization. * Trust client-side validation alone. * Use deprecated "mysql_*" functions. * Assume input is safe because it "looks" harmless. **Example:** Sanitizing an email address """php <?php $email = $_POST['email'] ?? ''; if (filter_var($email, FILTER_VALIDATE_EMAIL)) { $sanitizedEmail = filter_var($email, FILTER_SANITIZE_EMAIL); echo "Valid and Sanitized Email: " . htmlspecialchars($sanitizedEmail) . PHP_EOL; // Escape for output } else { echo "Invalid Email Address" . PHP_EOL; } ?> """ **Anti-Pattern:** Directly using "$_POST" values in a database query. """php <?php // Vulnerable code $username = $_POST['username']; $query = "SELECT * FROM users WHERE username = '$username'"; // SQL Injection vulnerability // Avoid the above approach ?> """ **Modern Approach:** Use prepared statements """php <?php $username = $_POST['username'] ?? ''; // Assuming $pdo is a PDO database connection $stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username"); $stmt->bindParam(':username', $username, PDO::PARAM_STR); $stmt->execute(); $user = $stmt->fetch(); if ($user) { echo "User found: " . htmlspecialchars($user['username']) . PHP_EOL; // Escape on output } else { echo "User not found." . PHP_EOL; } ?> """ ### 1.2. Output Encoding **Standard:** All data displayed to the user must be properly encoded to prevent XSS attacks. **Why:** Prevents malicious scripts from being injected into your website. **Do This:** * Use "htmlspecialchars()" or "htmlentities()" to escape HTML output. * Use appropriate escaping functions for other output formats (e.g., "json_encode()" for JSON, "urlencode()" for URLs). * Consider using a templating engine that automatically escapes output. Context aware escaping is optimal. **Don't Do This:** * Output user-controlled data without any encoding. * Rely solely on client-side escaping. * Disable escaping features in templating engines. **Example:** Encoding output """php <?php $userInput = "<script>alert('XSS');</script>"; $encodedOutput = htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8'); echo "Encoded Output: " . $encodedOutput . PHP_EOL; ?> """ **Anti-Pattern:** Directly displaying user input without encoding. """php <?php // Vulnerable code echo $_GET['name']; // XSS Vulnerability // Avoid! ?> """ **Modern Approach:** Using "htmlspecialchars()": """php <?php $name = $_GET['name'] ?? ''; echo htmlspecialchars($name, ENT_QUOTES, 'UTF-8'); // Properly encoded ?> """ ### 1.3. Authentication and Authorization **Standard:** Implement strong authentication and authorization mechanisms to control access to resources. **Why:** Prevents unauthorized access and protects sensitive data. **Do This:** * Use strong password hashing algorithms (e.g., "password_hash()" with "PASSWORD_DEFAULT" or "PASSWORD_ARGON2I"). * Implement proper session management (secure cookies, session fixation protection, session hijacking prevention). * Use role-based access control (RBAC) to define user permissions. * Implement multi-factor authentication (MFA) for critical accounts. Use modern authentication (e.g., WebAuthn). **Don't Do This:** * Store passwords in plain text or use weak hashing algorithms (e.g., MD5, SHA1). * Use default session settings without proper security configurations. * Grant excessive privileges to users. * Implement authentication yourself if a well vetted package (e.g. Laravel Sanctum/Passport, Symfony Security) can do it for you consistently. **Example:** Password hashing and verification. """php <?php $password = "SecretPassword123"; // NEVER hardcode actual passwords for testing in real projects. $hashedPassword = password_hash($password, PASSWORD_DEFAULT); echo "Hashed Password: " . $hashedPassword . PHP_EOL; $userInputPassword = "SecretPassword123"; if (password_verify($userInputPassword, $hashedPassword)) { echo "Password is valid!" . PHP_EOL; } else { echo "Invalid password." . PHP_EOL; } ?> """ **Anti-Pattern:** Storing passwords as plaintext. """php <?php // Vulnerable code $password = $_POST['password']; // $query = "INSERT INTO users (username, password) VALUES ('$username', '$password')"; //Avoid! ?> """ **Modern Approach:** Using "password_hash()" """php <?php $password = $_POST['password'] ?? ''; $hashedPassword = password_hash($password, PASSWORD_DEFAULT); // $query = "INSERT INTO users (username, password) VALUES ('$username', '$hashedPassword')"; // Correct way ?> """ ### 1.4. Session Management **Standard:** Employ secure session management techniques to protect user sessions from hijacking and fixation. **Why:** Prevents attackers from impersonating legitimate users. **Do This:** * Use "session_regenerate_id(true)" after successful login and periodically. * Set "session.cookie_httponly = true" to prevent JavaScript access to cookies. * Set "session.cookie_secure = true" to only transmit cookies over HTTPS. * Implement session timeouts. * Store session data server-side. **Don't Do This:** * Store sensitive information directly in cookies. * Rely solely on cookie values for authentication. * Use predictable session IDs. **Example:** Secure session configuration: """php <?php ini_set('session.cookie_httponly', true); ini_set('session.cookie_secure', true); session_start(); // Regenerate session ID upon login session_regenerate_id(true); ?> """ ### 1.5. Error Handling and Logging **Standard:** Implement robust error handling and logging mechanisms to identify and address security vulnerabilities. **Why:** Helps detect and respond to security incidents, and provides valuable debugging information. **Do This:** * Configure PHP to log errors to a secure location. * Use custom error handlers and exception handlers. * Log security-related events (e.g., login failures, unauthorized access attempts). * Avoid displaying sensitive error information to users in production environments. * Use a logging library (e.g., Monolog) for structured logging. **Don't Do This:** * Disable error reporting in production. * Display full error messages to users. * Store logs in a publicly accessible directory. **Example:** Custom error and exception handling """php <?php use Monolog\Logger; use Monolog\Handler\StreamHandler; // Create a logger $log = new Logger('app'); $log->pushHandler(new StreamHandler(__DIR__.'/app.log', Logger::WARNING)); set_error_handler(function($severity, $message, $file, $line){ global $log; $log->error("Error: $message in $file on line $line"); error_log("Error: $message in $file on line $line"); //Fallback, but prefer a well configured logger //return true; //Suppress standard error handling }); set_exception_handler(function($exception) use ($log) { $log->critical("Uncaught exception: " . $exception->getMessage() . " in " . $exception->getFile() . " on line " . $exception->getLine()); error_log("Uncaught exception: " . $exception->getMessage() . " in " . $exception->getFile() . " on line " . $exception->getLine()); // Optionally display a user-friendly error page http_response_code(500); echo "<h1>An unexpected error occurred. Please contact support.</h1>"; }); // Trigger an error try { throw new Exception("This is a test exception."); } catch (Exception $e) { // Exception is now handled by set_exception_handler throw $e; } ?> """ ### 1.6. File Handling Security **Standard:** Implement secure file handling practices to prevent unauthorized access to files and directories. **Why:** Prevents attackers from uploading malicious files, accessing sensitive data, or executing arbitrary code. **Do This:** * Validate file types based on content (mime type checking is not sufficient) and extensions. * Store uploaded files outside the webroot. Use a separate storage service (e.g. S3) where feasible. * Randomize file names to prevent filename guessing. * Set appropriate file permissions (e.g., 644 for files, 755 for directories). * Disable file execution in upload directories (using ".htaccess" or server configuration). **Don't Do This:** * Store sensitive files in the webroot. * Trust user-provided file names or extensions. * Allow unrestricted file uploads. * Use weak file permission settings. **Example:** Secure file upload """php <?php use Ramsey\Uuid\Uuid; $targetDir = __DIR__ . "/uploads"; if (!is_dir($targetDir)) { mkdir($targetDir, 0755, true); } $uploadedFile = $_FILES['file']; $originalFilename = basename($uploadedFile['name']); $fileSize = $uploadedFile['size']; $fileTmpName = $uploadedFile['tmp_name']; $fileType = $uploadedFile['type']; $fileError = $uploadedFile['error']; // Define allowed file types $allowedTypes = ['image/jpeg', 'image/png', 'application/pdf']; // Generate a unique filename $safeName = Uuid::uuid4()->toString(); $fileExtension = pathinfo($originalFilename, PATHINFO_EXTENSION); $newFilename = $safeName . '.' . $fileExtension; $targetFile = rtrim($targetDir, '/') . '/' . $newFilename; // Check if the file type is allowed if (!in_array($fileType, $allowedTypes)) { throw new Exception("Invalid file type."); } //Check filesize if($fileSize > 1000000){ throw new Exception("File too big."); } // Move the uploaded file to the destination directory if(move_uploaded_file($fileTmpName, $targetFile)){ echo "File uploaded successfully. New filename is " . htmlspecialchars($newFilename); } else { throw new Exception("There was an error uploading your file."); } ?> """ ### 1.7. Cross-Site Request Forgery (CSRF) Protection **Standard:** Implement CSRF protection to prevent unauthorized actions on behalf of authenticated users. **Why:** Prevents attackers from tricking users into performing unintended actions. **Do This:** * Generate and validate CSRF tokens for all state-changing requests (e.g., form submissions, API endpoints). * Store CSRF tokens in the session. * Use the "SameSite" attribute for cookies to mitigate CSRF risks. **Don't Do This:** * Rely solely on referrer checking. * Use predictable CSRF tokens. * Expose CSRF tokens in URLs. **Example:** CSRF protection. """php <?php session_start(); function generateCSRFToken() { return bin2hex(random_bytes(32)); } if (!isset($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = generateCSRFToken(); } function validateCSRFToken($token) { return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token); } // In your form: $csrfToken = $_SESSION['csrf_token']; echo "<input type='hidden' name='csrf_token' value='$csrfToken'>"; // On form submission: if ($_SERVER['REQUEST_METHOD'] === 'POST') { $submittedToken = $_POST['csrf_token'] ?? ''; if (validateCSRFToken($submittedToken)) { // Process the form data safely echo "CSRF token is valid. Processing form data..." . PHP_EOL; } else { // CSRF attack detected http_response_code(400); die("CSRF token is invalid."); } } ?> """ ### 1.8. Dependency Management **Standard:** Use Composer to manage dependencies and keep them up to date with the latest security patches. **Why:** Ensures that your application relies on secure and well-maintained libraries. **Do This:** * Define dependencies in "composer.json". * Use "composer update" regularly to update dependencies. * Monitor dependencies for known vulnerabilities using tools like "Roave Security Advisor". * Pin dependencies to specific versions or use version constraints to avoid unexpected breaking changes. **Don't Do This:** * Manually install or update dependencies. * Ignore security updates for dependencies. * Use outdated or unmaintained libraries. **Example:** Using Composer. """json { "require": { "php": "^8.1", "monolog/monolog": "^3.0", "ramsey/uuid": "^4.0" }, "require-dev": { "roave/security-advisories": "dev-latest" }, "config": { "sort-packages": true }, "scripts": { "security-check": [ "@composer audit" ] } } """ Run "composer install" or "composer update" after updating "composer.json". Also important to set up a process to run "composer audit" on a regular schedule. ### 1.9. Secure Configuration **Standard:** Securely configure PHP and the web server to minimize attack surface. **Why:** Reduces the risk of vulnerabilities being exploited. **Do This:** * Disable unnecessary PHP extensions. * Set strict file permissions. * Disable "expose_php". * Limit "allow_url_fopen" and "allow_url_include". * Use a web server configuration that restricts access (e.g., Apache's ".htaccess" or Nginx configuration). **Don't Do This:** * Use default configuration settings. * Enable unnecessary features. * Expose sensitive configuration information. ### 1.10. Security Headers **Standard:** Configure web server to set security related HTTP headers. **Why:** Adds additional layers of protection against various attacks. **Do This:** * "Strict-Transport-Security": Enforces HTTPS connections. Example: "Strict-Transport-Security: max-age=31536000; includeSubDomains; preload" * "X-Frame-Options": Protects against clickjacking attacks. Example: "X-Frame-Options: DENY" or "X-Frame-Options: SAMEORIGIN" * "X-Content-Type-Options": Prevents MIME-sniffing vulnerabilities. Example: "X-Content-Type-Options: nosniff" * "Content-Security-Policy": Controls resources the user agent is allowed to load. Example: "Content-Security-Policy: default-src 'self'" * "Referrer-Policy": Controls how much referrer information should be included with requests. Example: "Referrer-Policy: strict-origin-when-cross-origin" **Don't Do This:** * Rely solely on application-level security measures. * Ignore the defense-in-depth approach offered by security headers. * Use overly permissive policies that negate the protection offered by these headers. ## 2. Vulnerability-Specific Countermeasures ### 2.1. SQL Injection * **Use Prepared Statements:** Employ parameterized queries with PDO or MySQLi to prevent SQL injection. * **Escaping:** Use "PDO::quote()" or equivalent functions to properly escape user input when prepared statements aren't possible (rare). * **Least Privilege:** Database user should have least required privileges. ### 2.2. Cross-Site Scripting (XSS) * **Context-Aware Output Encoding:** Encode data based on its context (HTML, URL, JavaScript). Use specialized encoding functions like "htmlspecialchars()", "json_encode()", and "urlencode()". * **Content Security Policy (CSP):** Implement CSP to control the sources from which resources can be loaded. ### 2.3. Command Injection * **Avoid System Calls:** Minimize the use of "system()", "exec()", "shell_exec()", and similar functions. * **Input Validation:** If system calls are necessary, strictly validate and sanitize user input. Use "escapeshellarg()" and "escapeshellcmd()" carefully. ### 2.4. File Inclusion * **Avoid Dynamic Includes:** Avoid using variables in "include" or "require" statements. * **Whitelist:** If dynamic includes are unavoidable, create a strict whitelist of allowed files. * **Restrict Access:** Ensure the included files do not contain executable code or sensitive data. ### 2.5. Session Hijacking * **Regenerate Session IDs:** Call "session_regenerate_id(true)" after successful login and periodically. * **Secure Cookies:** Set "session.cookie_httponly" and "session.cookie_secure" in "php.ini". * **Session Timeouts:** Implement session timeouts to limit the lifespan of sessions. ## 3. Advanced Security Techniques ### 3.1. Encryption * **Use Libraries:** Use established encryption libraries like OpenSSL or Sodium. * **Authenticated Encryption:** Use authenticated encryption modes (e.g., AES-GCM) to ensure data integrity. * **Key Management:** Store encryption keys securely and use proper key management practices. Consider using a Key Management Service (KMS). ### 3.2. Security Auditing * **Code Reviews:** Conduct regular code reviews to identify potential security vulnerabilities. * **Static Analysis:** Use static analysis tools (e.g., Psalm, Phan) to detect code quality and security issues. * **Penetration Testing:** Perform periodic penetration testing to assess the security of your application in a real-world scenario. ### 3.3. Rate Limiting * **Implement Rate Limiting:** Protect against brute-force attacks and denial-of-service (DoS) by implementing rate limiting on critical endpoints. * **Monitor and Block:** Employ tools to monitor for suspicious activity and block malicious IP addresses. ## 4. Continuous Security Improvement ### 4.1. Stay Updated * **PHP Updates:** Regularly update PHP to the latest stable version to benefit from security patches and improvements. * **Security Newsletters:** Subscribe to security newsletters and mailing lists to stay informed about new vulnerabilities and best practices. * **Security Training:** Invest in security training for your development team to improve their knowledge and awareness. ### 4.2. Incident Response * **Incident Response Plan:** Develop a comprehensive incident response plan to handle security breaches and incidents effectively. * **Reporting:** Establish a clear process for reporting security vulnerabilities and incidents. * **Post-Mortem Analysis:** Conduct thorough post-mortem analysis after security incidents to identify root causes and implement preventive measures. This comprehensive coding standard provides a solid foundation for secure PHP development. By adhering to these guidelines, developers can significantly reduce the risk of vulnerabilities and build more robust and secure applications. Remember, security is an ongoing process that requires continuous attention and improvement. It's also critical to remember defense in depth - security measures should be layered, so if one fails, others still provide protection.
# Tooling and Ecosystem Standards for PHP This document outlines the recommended tools, libraries, and development practices within the PHP ecosystem to ensure maintainability, performance, security, and consistency across projects. It is designed to guide developers and provide context for AI coding assistants. This rule focuses specifically on the "Tooling and Ecosystem" aspects of PHP development, ensuring it is distinct from other rules. ## 1. Dependency Management with Composer ### Standard Use Composer for all project dependency management. Define dependencies explicitly in "composer.json" and utilize "composer.lock" to ensure consistency across environments. Take advantage of Composer's autoloading capabilities. **Do This:** * Use Composer for all dependencies. * Specify semantic versioning constraints (e.g., "^2.0", "~2.1") in "composer.json". * Commit "composer.json" and "composer.lock" to version control. * Use Composer's autoloading. * Keep dependencies up to date with "composer update" regularly, testing thoroughly after updates. * Leverage "composer install --optimize-autoloader --no-dev" in production. **Don't Do This:** * Manually download and include libraries. * Use wildcard version constraints (e.g., "*"). * Commit the "vendor" directory to version control. * Ignore Composer's autoloading and manually include files. * Neglect dependency updates for extended periods. **Why This Matters:** Composer centralizes dependency management, simplifying installation, updates, and resolving conflicts. Semantic versioning allows controlled updates while minimizing breaking changes. The "composer.lock" file ensures that all team members and environments use the exact same versions of dependencies, preventing unexpected behavior differences. Optimized autoloading enhances performance by reducing file system operations. **Code Example:** """json // composer.json { "require": { "php": "^8.1", "symfony/http-foundation": "^6.0", "doctrine/orm": "^3.0" }, "require-dev": { "phpunit/phpunit": "^9.0", "roave/security-advisories": "dev-latest" }, "autoload": { "psr-4": { "App\\": "src/" } }, "autoload-dev": { "psr-4": { "Tests\\": "tests/" } }, "config": { "sort-packages": true }, "scripts": { "test": "phpunit" } } """ This "composer.json" file specifies the required PHP version and project dependencies, along with development dependencies for testing. The "autoload" section defines the mapping between namespaces and directories. "scripts" allows you to define common commands. "config" allow you to define configurations. **Anti-Pattern:** Manually downloading libraries and including them in your project. This approach quickly leads to dependency conflicts, difficulty in updating libraries, and a lack of consistent dependency management. ## 2. Framework Selection ### Standard Choose a well-maintained PHP framework (e.g., Laravel, Symfony) that aligns with project requirements. Adhere to the framework's conventions and best practices. Evaluate the trade-offs of micro-frameworks (e.g., Slim, Lumen) vs. full-stack frameworks. **Do This:** * Select a framework that suits the project's scope and complexity. * Follow the framework's recommended project structure. * Utilize the framework's built-in features for routing, templating, ORM, etc., instead of reinventing the wheel. * Stay updated with the framework's latest releases and security patches. * Use the frameworks provided testing tools **Don't Do This:** * Build your own custom framework unless absolutely necessary. * Ignore the framework's conventions and create a disorganized project structure. * Try to mix and match components from different frameworks. * Use outdated or unsupported frameworks. **Why This Matters:** Established frameworks provide a solid foundation, promoting code reuse, consistency, and security. They handle common tasks, allowing developers to focus on business logic. Following framework conventions improves collaboration and maintainability. Selecting and using the right framework helps with efficiency and performance. **Code Example (Laravel):** """php // app/Http/Controllers/UserController.php namespace App\Http\Controllers; use App\Models\User; use Illuminate\Http\Request; class UserController extends Controller { public function index() { $users = User::all(); return view('users.index', ['users' => $users]); } public function store(Request $request) { $validatedData = $request->validate([ 'name' => 'required|max:255', 'email' => 'required|email|unique:users', 'password' => 'required|min:8', ]); $user = User::create($validatedData); return redirect('/users')->with('success', 'User created successfully.'); } } """ This Laravel controller demonstrates using the framework's ORM (Eloquent) to retrieve and create users. It also shows request validation. Following Laravel's MVC structure ("app/Http/Controllers", "app/Models", "resources/views") maintains consistency and improves readability. **Anti-Pattern:** Trying to build a web application from scratch without using a framework. This leads to duplicated effort, inconsistent code, increased security risks, and difficulty scaling. ## 3. Code Analysis Tools ### Standard Integrate static analysis tools (e.g., PHPStan, Psalm) into your development workflow to identify potential bugs, type errors, and code quality issues early on. Use code-sniffing tools (e.g., PHP_CodeSniffer) to enforce coding standards. **Do This:** * Configure static analysis tools with a strict level of checking. * Run static analysis regularly as part of your CI/CD pipeline. * Address all reported issues by either fixing the code or suppressing the error with a clear explanation. * Use a common coding standard like PSR-12 with PHP_CodeSniffer. * Automate code style checks with tools like PHP CS Fixer. **Don't Do This:** * Ignore warnings from static analysis tools. * Disable static analysis tools to avoid fixing issues. * Commit code with unresolved code style violations. **Why This Matters:** Static analysis and code-sniffing tools automatically detect potential problems in your code before runtime, improving code quality, reducing bugs, and ensuring adherence to coding standards. This leads to more reliable, maintainable, and consistent codebases. **Code Example (PHPStan):** 1. Install PHPStan using Composer: """bash composer require --dev phpstan/phpstan """ 2. Create a "phpstan.neon" configuration file: """yaml parameters: level: 7 paths: - src - tests excludePaths: - src/Migrations # Example exclude """ 3. Run PHPStan: """bash ./vendor/bin/phpstan analyse """ **Anti-Pattern:** Skipping static analysis because it takes time to fix the reported issues. The initial investment in fixing these issues will save significant time and effort in the long run by preventing bugs and improving code quality. ## 4. Debugging and Profiling ### Standard Use Xdebug for debugging and profiling PHP applications. Configure Xdebug to integrate with your IDE. Utilize profiling tools (e.g., Blackfire.io) to identify performance bottlenecks. **Do This:** * Install and configure Xdebug properly. * Use Xdebug's stepping and breakpoint features to understand code execution. * Set "xdebug.mode=debug" and "xdebug.start_with_request=yes" or "trigger" in your php.ini file (development only). Use "off" in production. * Use Blackfire.io or similar tools to profile slow requests. * Analyze profiling results to identify and optimize slow code paths. Use "xhprof" for server side profiling. * Use a code formatter that supports PSR-12 to keep the code readable and maintainable. **Don't Do This:** * Rely solely on "var_dump" or "print_r" for debugging. * Leave Xdebug enabled in production. * Ignore performance bottlenecks identified by profiling tools. **Why This Matters:** Xdebug provides advanced debugging features that make it easier to understand code execution and identify bugs. Profiling helps you pinpoint performance bottlenecks, allowing you to optimize your code for faster execution. **Code Example (Xdebug):** 1. Install Xdebug (usually via PECL). 2. Configure "php.ini": """ini zend_extension=xdebug.so xdebug.mode=debug xdebug.start_with_request=yes xdebug.client_host=host.docker.internal xdebug.client_port=9003 """ 3. Set breakpoints in your IDE. 4. Run your PHP script and trigger a debugging session from your IDE, or using a browser extension. The "xdebug.client_host=host.docker.internal" setting is crucial when running PHP in a Docker container, allowing Xdebug to connect back to the host machine. The port can be different on some setups too but is usually 9003 or 9000. **Anti-Pattern:** Leaving Xdebug enabled with "xdebug.mode=debug" and "xdebug.start_with_request=yes" in production. This can significantly impact performance and expose debugging information. ## 5. Templating Engines ### Standard Use a templating engine (e.g., Twig, Blade) to separate presentation logic from business logic. Avoid embedding PHP code directly within templates. Utilize template inheritance and components for code reuse. **Do This:** * Choose a templating engine that meets your project's needs. * Use template variables to pass data from PHP to templates. * Utilize template inheritance to create reusable layouts. * Use components or macros to encapsulate reusable UI elements. Encapsulate complex logic within a view composer. * Escapes all output to prevent XSS **Don't Do This:** * Embed complex PHP logic directly in templates. * Use "echo" or "print" statements directly in templates. * Ignore the templating engine's security features (e.g., auto-escaping). **Why This Matters:** Templating engines improve code organization, readability, and security by separating concerns. Template inheritance and components promote code reuse and maintainability. **Code Example (Twig):** """twig {# templates/users/index.html.twig #} {% extends 'base.html.twig' %} {% block body %} <h1>User List</h1> <ul> {% for user in users %} <li>{{ user.name }} - {{ user.email }}</li> {% endfor %} </ul> {% endblock %} """ """twig {# templates/base.html.twig #} <!DOCTYPE html> <html> <head> <title>{% block title %}My App{% endblock %}</title> </head> <body> <header> {% include 'header.html.twig' %} </header> <main> {% block body %}{% endblock %} </main> <footer> {% include 'footer.html.twig' %} </footer> </body> </html> """ This example demonstrates Twig template inheritance, where "users/index.html.twig" extends the "base.html.twig" layout and defines the content for the "body" block. **Anti-Pattern:** Embedding PHP code directly within templates. This makes templates difficult to read, maintain, and debug. It also increases the risk of security vulnerabilities. ## 6. Caching Strategies ### Standard Implement caching strategies to improve application performance. Use various caching mechanisms (e.g., object caching, page caching, opcode caching) based on the specific needs of your application. **Do This:** * Enable opcode caching (e.g., OpCache) in production. * Use object caching (e.g., Redis, Memcached) to store frequently accessed data. * Implement page caching for static or infrequently updated pages. * Use HTTP caching headers (e.g., "Cache-Control", "Expires") to leverage browser caching. * Invalidate the cache when data changes. Configure your ORM to use caching layer if possible. **Don't Do This:** * Neglect caching altogether. * Cache sensitive data without proper encryption. * Invalidate the cache too aggressively, leading to cache thrashing. * Use overly long cache durations for dynamic content. **Why This Matters:** Caching reduces database load, improves response times, and enhances the overall user experience. Opcode caching optimizes PHP execution by caching compiled code. Object caching stores frequently accessed data in memory. Page caching serves static content directly without executing PHP. Browser caching leverages the client's browser to store static assets. **Code Example (Redis Object Caching):** """php use Redis; $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $key = 'user:123'; $user = $redis->get($key); if (!$user) { // Fetch user data from database $userData = $db->getUser(123); // Serialize the data before storing in Redis $user = serialize($userData); $redis->set($key, $user, 3600); // Cache for 1 hour } else { //Unserialize cached data $user = unserialize($user); } // Now you can work with the $user object """ **Anti-Pattern:** Not using any caching mechanisms in a high-traffic application. This puts unnecessary load on the database and slows down the application, resulting in a poor user experience. ## 7. API Clients and HTTP Requests ### Standard Use a robust HTTP client library (e.g., Guzzle) for making API requests. Handle errors and exceptions gracefully. Implement retry mechanisms for transient failures. **Do This:** * Use Guzzle or Symfony's HttpClient component for HTTP requests. * Set appropriate timeouts for API requests. * Implement error handling for API responses (e.g., check status codes). * Use dependency injection to manage HTTP client instances. * Implement exponential backoff retry mechanisms for transient errors * Use PSR-7 interfaces when appropriate to define requests and responses. **Don't Do This:** * Use "file_get_contents" or "curl" directly for API requests. * Ignore errors from API responses. * Retry requests indefinitely without a limit. **Why This Matters:** HTTP client libraries provide a clean and consistent API for making HTTP requests, handling authentication, setting headers, and processing responses. Error handling ensures that your application can gracefully recover from API failures. Retry mechanisms improve resilience to transient errors. **Code Example (Guzzle):** """php use GuzzleHttp\Client; use GuzzleHttp\Exception\RequestException; $client = new Client(['base_uri' => 'https://api.example.com']); try { $response = $client->request('GET', '/users/123', [ 'headers' => [ 'Authorization' => 'Bearer YOUR_API_KEY', ], 'timeout' => 5, // Timeout in seconds ]); $statusCode = $response->getStatusCode(); $body = $response->getBody()->getContents(); if ($statusCode === 200) { $userData = json_decode($body); // Process User Data } else { error_log("API request failed with status code: " . $statusCode); } } catch (RequestException $e) { error_log("API request exception: " . $e->getMessage()); // Handle request exception (e.g., network error, timeout) } """ **Anti-Pattern:** Using "file_get_contents" to make external API calls. This is insecure and lacks the flexibility of a dedicated HTTP client library. It doesn't handle complex scenarios such as timeouts, authentication or error handling easily. ## 8. Database Interaction and ORMs ### Standard Use an ORM (e.g., Doctrine, Eloquent) to interact with databases. Avoid writing raw SQL queries directly in your code. Utilize database migrations to manage schema changes. **Do This:** * Choose an ORM that suits your project's needs. * Use the ORM's query builder or repository pattern for data access. * Define database schema migrations for all schema changes. * Use prepared statements or parameterized queries to prevent SQL injection. * Use connection pools to improve performance **Don't Do This:** * Write raw SQL queries directly in your code. * Modify the database schema manually without using migrations. * Store sensitive data in plain text in the database. * Ignore the ORM's performance optimization features (e.g., eager loading, caching). **Why This Matters:** ORMs abstract database interactions, providing a more object-oriented and maintainable way to work with data. Database migrations ensure that schema changes are version-controlled and can be applied consistently across environments. Proper use of parametrized queries prevents SQL injection attacks. **Code Example (Doctrine):** """php use Doctrine\ORM\EntityManager; use App\Entity\User; /** @var EntityManager $entityManager */ $user = $entityManager->find(User::class, 123); if (!$user) { $user = new User(); $user->setName('John Doe'); $user->setEmail('john.doe@example.com'); $entityManager->persist($user); $entityManager->flush(); } echo $user->getName(); """ **Anti-Pattern:** Writing raw SQL queries directly within your PHP code. This makes your code harder to maintain, increases the risk of SQL injection vulnerabilities, and reduces database portability. ## 9. Queue Systems ### Standard Utilize queue systems (e.g., RabbitMQ, Redis Queue, Beanstalkd) for asynchronous task processing. Decouple time-consuming or non-critical tasks from the main request lifecycle **Do This:** * Select a reliable queue system that fits your project's architecture. * Push jobs to the queue for tasks that don't need immediate processing. * Implement robust error handling and retry mechanisms for failed jobs. * Monitor queue health and performance. * Use dedicated worker processes to consume tasks from the queue. **Don't Do This:** * Perform time-consuming tasks directly within the request lifecycle. * Ignore failed jobs or lose messages in the queue. * Overload the queue system with too many jobs. **Why This Matters:** Queue systems improve application responsiveness and scalability by offloading time-consuming tasks to background processes. They provide a reliable way to process asynchronous tasks, such as sending emails, processing images, or generating reports. **Code Example (Laravel Queue with Redis):** 1. Configure the "queue" connection in "config/queue.php" to use Redis. 2. Create a job class: """php namespace App\Jobs; use App\Models\User; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Mail; class SendWelcomeEmail implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; protected $user; public function __construct(User $user) { $this->user = $user; } public function handle() { Mail::to($this->user->email)->send(new WelcomeEmail($this->user)); } } """ 3. Dispatch the queue job: """php use App\Jobs\SendWelcomeEmail; use App\Models\User; $user = User::find($userId); SendWelcomeEmail::dispatch($user); """ **Anti-Pattern:** Performing slow tasks inline within the web request. This causes the user to wait, reduces responsiveness, and can lead to timeouts. ## 10. Logging and Monitoring ### Standard Implement comprehensive logging and monitoring to track application behavior, identify errors, and detect performance issues. Use a centralized logging system and monitoring tools. **Do This:** * Use a logging library (e.g., Monolog) to generate structured log messages. * Log errors, warnings, and informational messages. * Use appropriate log levels to categorize messages. * Send logs to a centralized logging system (e.g., ELK stack, Graylog). * Use monitoring tools (e.g., New Relic, DataDog) to track application performance. * Set up alerts for critical errors or performance degradation. **Don't Do This:** * Rely solely on "var_dump" or "print_r" for debugging in production. * Log sensitive information without proper redaction. * Ignore error logs or performance alerts. **Why This Matters:** Logging and monitoring provide valuable insights into application behavior, helping you identify and resolve issues quickly. Centralized logging allows you to aggregate and analyze logs from multiple sources. Monitoring tools provide real-time performance metrics and alerts. **Code Example (Monolog):** """php use Monolog\Logger; use Monolog\Handler\StreamHandler; // Create a log channel $log = new Logger('my_app'); $log->pushHandler(new StreamHandler('/path/to/your.log', Logger::WARNING)); // Add records to the log $log->warning('Foo'); $log->error('Bar'); """ **Anti-Pattern:** Not logging errors or warnings in production. This makes it difficult to diagnose and resolve issues, leading to prolonged downtime and frustrated users. This document provides a comprehensive set of guidelines for using tooling and ecosystem components effectively in PHP development, focusing on current best practices for modern codebases. Use this document as a guide for developers and as context for AI coding assistants.