PHP OPCache: The Secret Weapon for Laravel Performance Boost

Harish Kumar · · 3182 Views
PHP OPCache: The Secret Weapon for Laravel Performance Boost

OPCache, a built-in PHP opcode cache, is a powerful tool for significantly improving Laravel application speed. This guide will demonstrate how to effectively utilize OPCache to improve Laravel performance, decrease server load, and enhance overall application responsiveness.

Understanding OPCache

OPCache is a PHP extension designed to improve the performance of PHP scripts by storing precompiled script bytecode in shared memory. This eliminates the need for PHP to load and parse scripts on each request.

How OPCache Works

  1. Compilation Cache: When a PHP script is executed, OPCache stores the compiled bytecode in memory.

  2. Memory Storage: Subsequent requests for the same script are served from memory, avoiding disk I/O and compilation overhead.

  3. Automatic Invalidation: OPCache automatically invalidates and recompiles scripts when changes are detected.

Benefits of Using OPCache

  1. Reduced Load Times: Decreases the time needed to load and execute PHP scripts.

  2. Lower CPU Usage: Reduces the CPU load by serving precompiled scripts.

  3. Improved Scalability: Enhances the ability to handle more requests with the same server resources.

  4. Cost Efficiency: Lowers server costs by optimizing resource usage.

Installing and Enabling OPCache

1. Install OPCache: 

Make sure that OPCache is installed on your server. You can check this by listing all installed modules for your PHP setup using the command:

php --modules

If OPCache is not installed, you can do so by running the following command:

sudo apt install php8.x-opcache

Make sure to replace php8.x-opcache with the appropriate version you are using, such as php8.3-opcache.

2. Enable OPCache:

To configure OPCache via the php.ini file, you can start by locating the php.ini file, which is the main configuration file for PHP. The location of this file may vary depending on your server setup. You can find its location by running the following command:

php --ini

In our example, the file is located at:

/etc/php/8.3/cli/php.ini

Alternatively, you can create a separate configuration file specifically for OPCache settings, which is a common practice for keeping configurations organized. To do this, create a new file named 10-opcache.ini in the conf.d directory within your PHP configuration folder. This example assumes the directory is /etc/php/8.3/cli/conf.d/.

In our example, the file is located at:

/etc/php/8.3/cli/conf.d/10-opcache.ini

The 10 prefix ensures the file is loaded in the correct order.

Now, add the following configurations to enable OPCache in your php.ini file:

; Enable OPCache extension
zend_extension=opcache.so

; Recommended OPCache settings
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.revalidate_freq=2
opcache.fast_shutdown=1
opcache.enable_cli=1
opcache.validate_timestamps=1
opcache.file_cache=/path/to/cache
opcache.file_update_protection=2
opcache.max_wasted_percentage=5

Understanding OPCache Settings

Here are additional OPCache settings and what they mean:

  1. opcache.enable=1: Enables OPCache for PHP.

  2. opcache.memory_consumption=128: Allocates 128MB of memory for storing precompiled scripts.

  3. opcache.interned_strings_buffer=8: Allocates 8MB for interned strings in memory.

  4. opcache.max_accelerated_files=10000: Sets the maximum number of files that can be cached.

  5. opcache.revalidate_freq=2: Sets the frequency (in seconds) for checking script timestamps to see if they have been updated.

  6. opcache.fast_shutdown=1: Enables fast shutdown to reduce memory usage when scripts are terminated.

  7. opcache.enable_cli=1: Enables OPCache for the CLI version of PHP. This is useful for speeding up long-running PHP scripts executed from the command line.

  8. opcache.validate_timestamps=1: When enabled, OPCache checks the timestamps of files to see if they have been updated. If a file is updated, it is recompiled. By default, it is enabled.

  9. opcache.file_cache=/path/to/cache: Specifies the directory where OPCache should store cached scripts if they cannot be stored in shared memory.

  10. opcache.file_update_protection=2: Ensures that cached scripts are not accessed until at least this many seconds have passed since they were last modified.

  11. opcache.max_wasted_percentage=5: The percentage of "wasted" memory (due to fragmentation, etc.) that OPCache can tolerate before it triggers a restart of the cache to reclaim memory.

Configuring OPCache for Laravel

To optimize OPCache for Laravel, fine-tuning the configuration parameters is essential. Here are some recommended settings:

; Increase memory consumption to handle more scripts
opcache.memory_consumption=256

; Higher number of interned strings buffer for better performance
opcache.interned_strings_buffer=16

; Max number of scripts that can be cached
opcache.max_accelerated_files=20000

; Frequency of file status checks (in seconds)
opcache.revalidate_freq=60

; Enable file cache for scripts that can't be stored in shared memory
opcache.file_cache=/tmp

; Enable optimization for faster execution
opcache.opt_debug_level=0

Creating a Laravel Preload Script

Preloading allows you to load a set of PHP files into memory on startup, making them available for all requests. This can further reduce loading times for frequently used classes.

Step-by-Step Guide to Create a Preload Script

1. Create a Preload Script: Create a preload.php file in the root of your Laravel application.

<?php

require_once __DIR__ . '/vendor/autoload.php';

class Preloader
{
    private array $ignores = [];

    private static int $count = 0;

    private array $paths;

    private array $fileMap;

    public function __construct(string ...$paths)
    {
        $this->paths = $paths;

        // We'll use composer's classmap
        // to easily find which classes to autoload,
        // based on their filename
        $classMap = require __DIR__ . '/vendor/composer/autoload_classmap.php';

        $this->fileMap = array_flip($classMap);
    }

    public function paths(string ...$paths): Preloader
    {
        $this->paths = array_merge(
            $this->paths,
            $paths
        );

        return $this;
    }

    public function ignore(string ...$names): Preloader
    {
        $this->ignores = array_merge(
            $this->ignores,
            $names
        );

        return $this;
    }

    public function load(): void
    {
        // We'll loop over all registered paths
        // and load them one by one
        foreach ($this->paths as $path) {
            $this->loadPath(rtrim($path, '/'));
        }

        $count = self::$count;

        echo "[Preloader] Preloaded {$count} classes" . PHP_EOL;
    }

    private function loadPath(string $path): void
    {
        // If the current path is a directory,
        // we'll load all files in it
        if (is_dir($path)) {
            $this->loadDir($path);

            return;
        }

        // Otherwise we'll just load this one file
        $this->loadFile($path);
    }

    private function loadDir(string $path): void
    {
        $handle = opendir($path);

        // We'll loop over all files and directories
        // in the current path,
        // and load them one by one
        while ($file = readdir($handle)) {
            if (in_array($file, ['.', '..'])) {
                continue;
            }

            $this->loadPath("{$path}/{$file}");
        }

        closedir($handle);
    }

    private function loadFile(string $path): void
    {
        // We resolve the classname from composer's autoload mapping
        $class = $this->fileMap[$path] ?? null;

        // And use it to make sure the class shouldn't be ignored
        if ($this->shouldIgnore($class)) {
            return;
        }

        // Finally we require the path,
        // causing all its dependencies to be loaded as well
        require_once($path);

        self::$count++;

        echo "[Preloader] Preloaded `{$class}`" . PHP_EOL;
    }

    private function shouldIgnore(?string $name): bool
    {
        if ($name === null) {
            return true;
        }

        foreach ($this->ignores as $ignore) {
            if (strpos($name, $ignore) === 0) {
                return true;
            }
        }

        return false;
    }
}

(new Preloader())
    ->paths(__DIR__ . '/vendor/laravel')
    ->ignore(
        \Illuminate\Filesystem\Cache::class,
        \Illuminate\Log\LogManager::class,
        \Illuminate\Http\Testing\File::class,
        \Illuminate\Http\UploadedFile::class,
        \Illuminate\Support\Carbon::class,
    )
    ->load();

2. Update PHP Configuration: Add the preload script to your PHP configuration (php.ini).

; Path to the preload script
opcache.preload=/path/to/your/laravel/project/preload.php
opcache.preload_user=www-data

3. Restart Web Server: After modifying the php.ini file, restart your web server to apply the changes.

- For Apache:

sudo systemctl restart apache2

For Nginx with PHP-FPM:

sudo systemctl restart php8.3-fpm
sudo systemctl restart nginx

Checking OPCache Status

To verify if OPCache is working as expected and to inspect preloading details, create a PHP script named opcache.php in your Laravel project's root directory with the following content:

<?php

$status = opcache_get_status();

print_r($status);

Run this script from the command line:

php opcache.php

The output will provide detailed information about OPCache usage, configuration, and preloaded scripts. Look for the statistics section to verify preloading is working as intended.

Flushing OPCache

To clear the OPCache, you have two primary options:

1. Restart PHP or Web Server:

  1. This is the most straightforward method to completely reset the OPCache.

  2. However, it might disrupt ongoing requests.

2. Using a PHP Script:

  1. Create a PHP script with the following content:

opcache_reset();

echo 'OPCache flushed successfully.';
  1. Execute this script to clear the OPCache without restarting the server.

Monitoring OPCache

To ensure OPCache is functioning correctly and efficiently, you can monitor its performance using tools like opcache-gui or opcache-status.

Using opcache-gui

1. Install opcache-gui: Download and install opcache-gui from its GitHub repository.

git clone https://github.com/amnuts/opcache-gui.git /var/www/html/opcache-gui

2. Access opcache-gui: Open your browser and navigate to http://your-domain/opcache-gui.

Using opcache-status

1. Install opcache-status: Download and install opcache-status from its GitHub repository.

git clone https://github.com/rlerdorf/opcache-status.git /var/www/html/opcache-status

2. Access opcache-status: Open your browser and navigate to http://your-domain/opcache-status.

Additional Tips for Optimizing Laravel Performance

1. Config Caching: Cache your configuration files.

php artisan config:cache

2. Route Caching: Cache your routes to improve performance.

php artisan route:cache

3. View Caching: Pre-compile all Blade views.

php artisan view:cache

4. Optimize Composer Autoloader: Use optimized Composer autoloader.

composer dump-autoload --optimize

Performance Improvement with OPCache

By implementing OPCache and following these additional optimization tips, you can significantly boost the performance of your Laravel application. Here’s how OPCache improves performance:

  1. Faster Load Times: By storing precompiled script bytecode in memory, OPCache reduces the time needed to load and execute PHP scripts, resulting in faster response times.

  2. Reduced CPU Usage: Serving precompiled scripts directly from memory lowers CPU usage, as the server does not need to recompile scripts on each request.

  3. Improved Scalability: With reduced load times and lower CPU usage, your Laravel application can handle more requests with the same server resources, improving scalability.

  4. Enhanced Resource Utilization: Optimizing memory consumption and reducing wasted memory through OPCache settings helps in better resource management, leading to cost savings.

By leveraging OPCache and following best practices for Laravel performance optimization, you can ensure your application runs efficiently, providing a better user experience and reducing server costs.

.

🔥 Don't be left behind, get the Spec Coder VSCode Extension now

PHP OPCache: The Secret Weapon for Laravel Performance Boost
0

Please login or create new account to add your comment.

0 comments
You may also like:

PHP 8.4 Property Hooks: The Ultimate Guide for Developers

PHP 8.4, coming in November 2024, introduces a new feature called property hooks. This feature makes it easier to work with class properties by allowing you to define custom behavior (...)
Harish Kumar

Laracon US 2024: Laravel 11 Minor Features That Enhance Performance

At Laracon US 2024, Taylor Otwell and the Laravel team introduced a series of "minor" features for Laravel 11 that are anything but minor. These enhancements, while not headline-grabbing (...)
Harish Kumar

PHP Security Guide: Strategies for Safe and Secure Code

PHP is one of the most widely used server-side scripting languages for web development, powering millions of websites and applications. Its popularity is largely due to its ease (...)
Harish Kumar

How to Use DTOs for Cleaner Code in Laravel, Best Practices and Implementation Guide

When developing APIs in Laravel, ensuring your responses are clear, concise, and consistent is crucial for creating a maintainable and scalable application. One effective way to (...)
Harish Kumar

Data Transfer Objects (DTOs) in PHP: Streamlining Data Flow and Enhancing Code Clarity

Data Transfer Objects (DTOs) are simple objects used to transfer data between software application subsystems. They help in encapsulating data and reducing the number of method (...)
Harish Kumar

Data Type Validation in Laravel Collections with the `ensure()` Method

Before moving on to the ensure() method, let us first know what Laravel Collections actually are. These are wrappers of PHP arrays, offering a fluent and helpful interface in interacting (...)
Harish Kumar