# Welcome

Hi there, welcome to the Docket Cache Documentation.

## Docket Cache

The Docket Cache documentation.

* [About](/about)
* [Installation](/installation)
* [Constants](/constants)
* [WP-CLI](/wp-cli)
* [Admin Interface](/admin-interface)
* [FAQ](/faq)

## Links

External links related to Docket Cache.

* [Main Website](https://docketcache.com)
* [Support Forum](https://wordpress.org/support/plugin/docket-cache/)
* [Plugin Repo](https://wordpress.org/plugins/docket-cache/)
* [Github Repo](https://github.com/nawawi/docket-cache)

## Resources

Additional external reference related to Web Hosting, WordPress caching and PHP Zend OPcache.

* [WordPress Hosting](/resources/wordpress-hosting)
* [Cache In WordPress](/resources/caching-in-wordpress)
* [OPcache Extension](/resources/opcache-extension)
* [OPcache Optimisations](/resources/opcache-optimisations)
* [Web Hosting I/O Usage](/resources/web-hosting-i-o-usage)


# About

About Docket Cache and some relevant information.

## Prologue

The Docket cache is a persistent WordPress Object Cache that is stored as a plain PHP code. Intends to provide an alternative option for those who can't use Redis or Memcached server.

Rather than using [serialize](https://www.php.net/manual/en/function.serialize.php) and [unserialize](https://www.php.net/manual/en/function.unserialize.php) a PHP object to store into flat files, this plugin stores data by converting the object into plain PHP code which results in faster data retrieval and better performance with Zend OPcache enabled.

## Manifesto

When it comes to reliable persistent Object Cache in WordPress, [Redis](https://redis.io) or [Memcached](https://memcached.org) comes on top. However, those solutions require knowledge of server and rarely available at low cost or shared hosting servers

The only solution is to store the object caches into files. With WordPress, exporting the PHP objects are not easy, most plugin that implements file-based solution will `serialize` and `unserialize` the object to store and retrieve the data.

Docket Cache takes a better approach by turning the object cache into plain PHP code. This solution is faster since WordPress can use the cache directly without running other operations.

## How Versions Work

Versions are as follows: Year.Month.Day

* Year: Two digits representation of a year.
* Month: Two digits representation of a month.
* Day: Two digits representation of a day.

## License

{% hint style="info" %}
Docket cache is an Open Source Software under the MIT License.
{% endhint %}

Docket Cache

Copyright (c) 2020-present [Nawawi Jamili](https://github.com/nawawi)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

## Credits

Some parts of the Docket Cache code are borrowed from different open-source projects.\
The full list can be found here [https://github.com/nawawi/docket-cache/blob/master/credits.tx](https://github.com/nawawi/docket-cache/blob/master/credits.txt).


# Installation

Docket Cache installation methods.

## Requirements

To use Docket Cache requires minimum:

* PHP 7.2.5
* WordPress 5.4
* Zend OPCache

## WordPress Plugin

1. In your WordPress admin click `Plugins -> Add New` **.**
2. Search plugins **Docket Cache** and click `Install Now`.
3. Click `Activate` or `Network Activate` in Multisite setups.
4. Click **Docket Cache** in the left menu to access the admin interface.

{% hint style="info" %}
Please wait around 5 seconds for Docket Cache ready to cache the objects.
{% endhint %}

## Manual Installation

1. Download the plugin as a [ZIP file](https://github.com/nawawi/docket-cache/archive/master.zip) from GitHub or from [WordPress Plugin Directory](https://wordpress.org/plugins/docket-cache/).
2. In your WordPress admin click `Plugins -> Add New -> Upload Plugin`.
3. Upload the ZIP file and Activate the plugin.
4. Click Activate or Network Activate in Multisite setups.
5. Click Docket Cache in the left menu to access the admin page.

## Via WP-CLI

[`WP-CLI`](http://wp-cli.org) is the official command-line interface for WordPress. You can install Docket Cache using the `wp` command like this:

```
wp plugin install docket-cache --activate
```

## Via Composer

The plugin is available as [Composer package](https://packagist.org/packages/nawawi/docket-cache) and can be installed via Composer from the root of your WordPress installation.

```
composer create-project -s dev --prefer-dist nawawi/docket-cache wp-content/plugins/docket-cache
```

## Via Git

Go to your WordPress plugins folder `cd wp-content/plugins`

```
git clone https://github.com/nawawi/docket-cache
```


# Constants

Docket Cache uses constants variable as main configuration methods.

`Updated: 08-Mar-2023 | v22.07.04`

Constants are like variables except that once they are defined they cannot be changed or undefined. To change the behaviour of Docket Cache, the following PHP constants can be defined in your `wp-config.php` file.

Docket Cache load the configuration by calling [`Constan::register_default()`](https://github.com/nawawi/docket-cache/blob/master/includes/src/Constans.php#L155) method that can be found in file [includes/src/Constans.php](https://github.com/nawawi/docket-cache/blob/master/includes/src/Constans.php). Some constant marks as @private and for internal use, changing it may result in unpredictable behaviour.

## DOCKET\_CACHE\_MAXTTL

Default object lifespan in seconds.

Only numbers between 86400 and 2419200 are allowed.\
Default: 345600 (4 days)

```php
define('DOCKET_CACHE_MAXTTL', 345600);
```

If there is no expire time was set to object or set to 0, Docket Cache will use this setting as an expiration time.

This setting does not apply to cache groups below if the value of seconds is lower than the predefined seconds.

| **Group**      | **Key**                                       | **Seconds**       |
| -------------- | --------------------------------------------- | ----------------- |
| site-transient | update\_plugins, update\_themes, update\_core | 2419200 (28 days) |
| site-transient | any                                           | 604800 (7 days)   |
| transient      | any                                           | 604800 (7 days)   |
| terms          | any                                           | 1209600 (14 days) |
| posts          | any                                           | 1209600 (14 days) |
| post\_meta     | any                                           | 1209600 (14 days) |
| comments       | any                                           | 1209600 (14 days) |
| options        | any                                           | 1209600 (14 days) |
| site-options   | any                                           | 1209600 (14 days) |

## DOCKET\_CACHE\_MAXSIZE

Set the maximum size of the object data in bytes, which can be store in a cache file.

Only size between 1048576 (1MB) and 10485760 (10MB) are allowed.\
Default: 3145728 (3MB)

```php
define('DOCKET_CACHE_MAXSIZE', 3145728);
```

#### Example of object data:

```php
[
    1606534363 => [
        'wp_version_check' => [
            '40cd750bba9870f18aada2478b24840a' => [
                'schedule' => 'twicedaily',
                'args' => [],
                'interval' => 43200,
            ],
        ],
    ],
    'version' => 2,
]
```

{% hint style="info" %}
The size of the cache file is slightly bigger than the object since it contains Docket Cache metadata and exported as plain PHP code.
{% endhint %}

#### Example of the cache file content:

```php
[
    'timestamp' => 1606492052,
    'site_id' => 1,
    'group' => 'options',
    'key' => 'cron',
    'type' => 'string',
    'timeout' => 1607701652,
    'data' => [
        1606534363 => [
            'wp_version_check' => [
                '40cd750bba9870f18aada2478b24840a' => [
                    'schedule' => 'twicedaily',
                    'args' => [],
                    'interval' => 43200,
                ],
            ],
        ],
        'version' => 2,
    ],
]
```

#### Docket Cache Metadata:

| Name        | Description               |
| ----------- | ------------------------- |
| timestamp   | Data creation time        |
| site\_id    | Site Id                   |
| network\_id | Network Id (on multisite) |
| group       | Object Cache group        |
| key         | Object Cache key          |
| type        | Object Cache Data type    |
| timeout     | Expiration time           |
| data        | Object Cache data         |

## DOCKET\_CACHE\_MAXSIZE\_DISK

Set the maximum size of the cache storage on disk.

The minimum required size is 104857600 bytes (100MB).\
Default: 524288000 (500MB)

```php
define('DOCKET_CACHE_MAXSIZE_DISK', 524288000);
```

## DOCKET\_CACHE\_MAXFILE

Set the maximum cache file can be store on disk.

Only numbers between 200 and 1000000 are allowed.\
Default: 50000

```php
define('DOCKET_CACHE_MAXFILE', 50000);
```

## DOCKET\_CACHE\_CHUNKCACHEDIR

Set to `true` to enable chunking cache files into smaller directories to avoid an excessive number of cache files in one directory.

Only enable it if you have difficulty clearing the cache manually or experience slowdowns when the cache becomes too large.\
Default:

```php
define('DOCKET_CACHE_CHUNKCACHEDIR', false);
```

## DOCKET\_CACHE\_MAXFILE\_LIVECHECK

Set to `true` to allow Docket Cache to monitor the cache file limit in real-time.\
Default:

```php
define('DOCKET_CACHE_MAXFILE_LIVECHECK', false);
```

## DOCKET\_CACHE\_EMPTYCACHE\_IGNORE

Set to `true` to enable excluding empty caches from being stored on disk.

Only enable it if you have an issue with inode/file limits.\
Default:

```php
define('DOCKET_CACHE_EMPTYCACHE_IGNORE', false);
```

## DOCKET\_CACHE\_PATH

Set the cache directory.\
Default:

```php
define('DOCKET_CACHE_PATH', WP_CONTENT_DIR.'/cache/docket-cache');
```

## DOCKET\_CACHE\_DATA\_PATH

Set the configuration directory.\
Default:

```php
define('DOCKET_CACHE_DATA_PATH', WP_CONTENT_DIR.'/docket-cache-data');
```

## DOCKET\_CACHE\_CONTENT\_PATH

Set the Docket Cache writable directory.\
Default:

```php
define('DOCKET_CACHE_CONTENT_PATH', WP_CONTENT_DIR);
```

By default, Docket Cache requires writable permission on WordPress **wp-content** directory for internal use. Defining this constant also change the default path for cache, configuration and object-cache Drop-In. The content path must exist and has proper permission. The `object-cache.php` Drop-In file needs to symlink with WordPress `wp-content/object-cache.php` or replace with wrapper file.

Please refer to the PHP [`open_basedir` ](https://www.php.net/manual/en/ini.core.php#ini.open-basedir)setting before set this constant.

#### Example:

```php
$ sudo mkdir -p /opt/dc-content
$ sudo chown apache:apache /opt/dc-content
$ sumod chmod 755 /opt/dc-content
$ sudo ln -s /opt/dc-content/object-cache.php /your-wp-path/wp-content/object-cache.php
```

#### Wrapper file:

```php
<?php
if (!\defined('ABSPATH')) {
    return;
}

if (!\defined('DOCKET_CACHE_CONTENT_PATH')) {
    return;
}

if ( !@is_file(DOCKET_CACHE_CONTENT_PATH.'/object-cache.php') ) {
    return;
}

@include_once DOCKET_CACHE_CONTENT_PATH.'/object-cache.php';
```

## DOCKET\_CACHE\_FLUSH\_DELETE

By default Docket Cache only empty the cache file when expire. Set to true to delete the cache file instead of truncate.\
Default:

```php
define('DOCKET_CACHE_FLUSH_DELETE', false);
```

## DOCKET\_CACHE\_FLUSH\_STALECACHE

Set to `true` to allow Garbage Collector (GC) immediately remove the stale cache abandoned by WordPress, WooCommerce and others after doing cache invalidation.\
Default:

```php
define('DOCKET_CACHE_FLUSH_STALECACHE', false);
```

## DOCKET\_CACHE\_STALECACHE\_IGNORE

Set to `true` to enable excluding stale cache created by WordPress, WooCommerce, and others from being stored on disk.\
Default:

```php
define('DOCKET_CACHE_STALECACHE_IGNORE', false);
```

Only enable it if you have an issue with inode/file limits.

## DOCKET\_CACHE\_GLOBAL\_GROUPS

Set the lists of groups cached at the network level in a Multisite setup.\
Default:

```php
define('DOCKET_CACHE_GLOBAL_GROUPS',
  [
    'blog-details',
    'blog-id-cache',
    'blog-lookup',
    'global-posts',
    'networks',
    'rss',
    'sites',
    'site-details',
    'site-lookup',
    'site-options',
    'site-transient',
    'users',
    'useremail',
    'userlogins',
    'usermeta',
    'user_meta',
    'userslugs'
  ]
);
```

## DOCKET\_CACHE\_IGNORED\_GROUPS

List of cache groups that should not be cached.\
Default:

```php
define('DOCKET_CACHE_IGNORED_GROUPS',
  [
    'counts',
    'plugins',
    'themes'
  ]
);
```

## DOCKET\_CACHE\_IGNORED\_KEYS

List of cache keys that should not be cached.\
Default: not set.

#### Example:

```php
define('DOCKET_CACHE_IGNORED_KEYS',['key1', 'key2']);
```

## DOCKET\_CACHE\_IGNORED\_GROUPKEY

List of cache groups and keys that should not be cached.\
Default: not set.

```php
define('DOCKET_CACHE_IGNORED_GROUPKEY',
  [
    'group1' => ['key1', 'key2'],
    'group2' => ['key1', 'key2']
  ]
);
```

## DOCKET\_CACHE\_LOG

Set to `true` or `false` to enable or disable cache log.\
Default:

```php
define('DOCKET_CACHE_LOG', false);
```

{% hint style="info" %}
The cache log intended to provide information on how the cache works. For performance and security concerns, deactivate if no longer needed.
{% endhint %}

## DOCKET\_CACHE\_LOG\_FILE

Set the log file.\
Default:

```php
define('DOCKET_CACHE_LOG_FILE', WP_CONTENT_DIR.'/.object-cache.log');
```

## DOCKET\_CACHE\_LOG\_TIME

Set the log time format when viewing. Available options utc, local, wp.\
Default:

```php
define('DOCKET_CACHE_LOG_TIME', 'utc');
```

## DOCKET\_CACHE\_LOG\_FLUSH

Set to `true` to empty the log file when the object cache is flushed.\
Default:

```php
define('DOCKET_CACHE_LOG_FLUSH', true);
```

## DOCKET\_CACHE\_LOG\_SIZE

Set the maximum size of the log file in bytes.\
Default: 10485760 (10MB)

```php
define('DOCKET_CACHE_LOG_SIZE', 10485760);
```

## DOCKET\_CACHE\_LOG\_ALL

By default, Docket Cache excludes it own process if WP\_DEBUG not defined as true.

Set to `true` or `false` to enable or disable to log all caches.\
Default:

```php
define('DOCKET_CACHE_LOG_ALL', false);
```

## DOCKET\_CACHE\_ADVCPOST

Set to `true` to enable Advanced Post Cache features that cache WP Queries for a post which results in faster data retrieval and reduced database workload.\
Default:

```php
define('DOCKET_CACHE_ADVCPOST', true);
```

{% hint style="info" %}
Since version 22.07.04, the Advanced Post Cache feature is only available for WordPress version 6.1 and below. Since it is already implemented in WordPress Core as WP\_Query caching.
{% endhint %}

## DOCKET\_CACHE\_ADVCPOSTTYPE

List of Post Types allowed for Advanced Post Cache.\
Default:

```php
define('DOCKET_CACHE_ADVCPOSTTYPE',
    [
        'post',
        'page',
        'attachment',
        'revision',
        'nav_menu_item',
        'custom_css',
        'customize_changeset',
        'oembed_cache',
        'user_request',
        'wp_block',
        'wp_template',
        'wp_template_part',
        'wp_global_styles',
        'wp_navigation',
    ]
);
```

{% hint style="info" %}
Since version 22.07.04, this constant only works for WordPress version 6.1 and below.
{% endhint %}

## DOCKET\_CACHE\_ADVCPOSTTYPE\_ALL

Set to true to allow all Post Types for Advanced Post Cache.\
Default:

```php
define('DOCKET_CACHE_ADVCPOSTTYPE_ALL', false);
```

{% hint style="info" %}
Since version 22.07.04, this constant only works for WordPress version 6.1 and below.
{% endhint %}

## DOCKET\_CACHE\_CRONOPTMZDB

Enable Database Tables optimization.

Available options: never, daily, weekly, monthly.\
Default:

```php
define('DOCKET_CACHE_CRONOPTMZDB', 'never');
```

## DOCKET\_CACHE\_WPOPTALOAD

Set to `true` or `false` to enable or disable Suspend WP Options Autoload features.\
Default:

```php
define('DOCKET_CACHE_WPOPTALOAD', false);
```

## DOCKET\_CACHE\_MISC\_TWEAKS

Set to `true` or `false` to enable or disable miscellaneous WordPress performance tweaks.\
Default:

```php
define('DOCKET_CACHE_MISC_TWEAKS', true);
```

## DOCKET\_CACHE\_WOOTWEAKS

Set to `true` or `false` to enable or disable miscellaneous WooCommerce tweaks.\
Default:

```php
define('DOCKET_CACHE_WOOTWEAKS', true);
```

## DOCKET\_CACHE\_WOOADMINOFF

WooCommerce Admin or Analytics page is a new JavaScript-driven interface for managing stores.

Set to true to disable WooCommerce Admin feature-related.\
Default:

```php
define('DOCKET_CACHE_WOOADMINOFF', false);
```

## DOCKET\_CACHE\_WOOWIDGETOFF

Set to true to disable WooCommerce Classic Widget feature.\
Default:

```php
define('DOCKET_CACHE_WOOWIDGETOFF', false);
```

## DOCKET\_CACHE\_WOOWPDASHBOARDOFF

Set to true to disable WooCommerce meta box in the WordPress Dashboard.\
Default:

```php
define('DOCKET_CACHE_WOOWPDASHBOARDOFF', false);
```

## DOCKET\_CACHE\_WOOCARTFRAGSOFF

Set to true to disable WooCommerce Cart Fragments feature.\
Default:

```php
define('DOCKET_CACHE_WOOCARTFRAGSOFF', false);
```

## DOCKET\_CACHE\_WOOADDTOCHARTCRAWLING

Set to true to enable prevent robots crawling add-to-cart links.\
Default:

```php
define('DOCKET_CACHE_WOOADDTOCHARTCRAWLING', true);
```

## DOCKET\_CACHE\_WOOEXTENSIONPAGEOFF

Set to true to disable WooCommerce Extensions Page feature.\
Default:

```php
define('DOCKET_CACHE_WOOEXTENSIONPAGEOFF', true);
```

## DOCKET\_CACHE\_POSTMISSEDSCHEDULE

Set to `true` or `false` to enable or disable Post Missed Schedule Tweaks features.\
Default:

```php
define('DOCKET_CACHE_POSTMISSEDSCHEDULE', false);
```

## DOCKET\_CACHE\_OPTERMCOUNT

Set to `true` or `false` to enable or disable Term Count Queries optimization features.\
Default:

```php
define('DOCKET_CACHE_OPTERMCOUNT', true);
```

## DOCKET\_CACHE\_OPTWPQUERY

Set to `true` to enable WordPress Core Query optimization features. Docket Cache will attempt to optimize WordPress core query when enabled.\
Default:

```php
define('DOCKET_CACHE_OPTWPQUERY', true);
```

## DOCKET\_CACHE\_LIMITBULKEDIT

Set to `true` or `false` to enable or disable the Bulk Edit Actions when reaching the listed item limit.\
Default:

```php
define('DOCKET_CACHE_LIMITBULKEDIT', false);
```

## DOCKET\_CACHE\_LIMITBULKEDIT\_LIMIT

Set a limit of items listed for Bulk Edit Actions.\
Default:

```php
define('DOCKET_CACHE_LIMITBULKEDIT_LIMIT', 100);
```

## DOCKET\_CACHE\_MOCACHE

Set to `true` to enable WordPress Translation Caching features that improve the performance of the Translation function.\
Default:

```php
define('DOCKET_CACHE_MOCACHE', false);
```

## DOCKET\_CACHE\_MENUCACHE

Set to true to enable WordPress Menu Caching features that improve the performance of the WordPress menus generation.\
Default:

```php
define('DOCKET_CACHE_MENUCACHE', false);
```

## DOCKET\_CACHE\_MENUCACHE\_TTL

Default Menu Cache lifespan in seconds.

Only numbers between 86400 and 2419200 are allowed.\
Default: 1209600 (14 days)

```php
define('DOCKET_CACHE_MENUCACHE_TTL', 1209600);
```

## DOCKET\_CACHE\_SIGNATURE

Set to `true` or `false` to enable or disable Docket Cache signature at HTML footer and Server Header.\
Default:

```php
define('DOCKET_CACHE_SIGNATURE', true);
```

## DOCKET\_CACHE\_PRECACHE

Set to `true` to enable Object Cache Precaching features that increase cache performance by early loading cached objects based on the current URL.\
Default:

```php
define('DOCKET_CACHE_PRECACHE', true);
```

## DOCKET\_CACHE\_PRECACHE\_MAXFILE

Set the maximum precache file can be store on disk.

Only numbers between 100 and 1000000 are allowed.\
Default: 100

```php
define('DOCKET_CACHE_PRECACHE_MAXFILE', 100);
```

## DOCKET\_CACHE\_PRECACHE\_MAXKEY

Set the maximum precache keys.\
Default: 20

```php
define('DOCKET_CACHE_PRECACHE_MAXKEYe', 20);
```

## DOCKET\_CACHE\_PRECACHE\_MAXGROUP

Set the maximum precache groups.\
Default: 20

```php
define('DOCKET_CACHE_PRECACHE_MAXGROUP', 20);
```

## DOCKET\_CACHE\_IGNORED\_PRECACHE

List of cache groups and keys that should not be precached.\
Default:

```php
define('DOCKET_CACHE_IGNORED_PRECACHE',
    [
     	'freemius' => 'fs_accounts',
        'options' => [
            'uninstall_plugins',
            'auto_update_plugins',
            'active_plugins',
            'cron',
            'litespeed_messages',
            'litespeed.admin_display.messages',
        ],
	'site-options' => [
            '1:auto_update_plugins',
            '1:active_sitewide_plugins',
        ],
    ]
);
```

## DOCKET\_CACHE\_PRELOAD

Set to `true` or `false` to enable or disable cache preloading. If set to `true`, this plugin will fetch predefined URL related to the admin page.

The preload only runs when doing a cache flush.\
Default:

```php
define('DOCKET_CACHE_PRELOAD', false);
```

## DOCKET\_CACHE\_PAGELOADER

Set to `true` or `false` to enable or disable Admin Page Loader features.\
Default:

```php
define('DOCKET_CACHE_PAGELOADER', true);
```

## DOCKET\_CACHE\_TRANSIENTDB

By default WordPress stores Transients Cache in Database. When a persistent object cache is available, it switches Transients from Database to the object cache.

Some plugins use Transients not in the right way, they store too big data without expiration time. Without expiration, WordPress will place the Transient in the "alloptions" variable. It will make persistent object caching solutions like Docket Cache unable to handle it.

Set to `true` or `false` to enable or disable retaining Transients in the Database.\
Default:

```php
define('DOCKET_CACHE_TRANSIENTDB', false);
```

## DOCKET\_CACHE\_IGNORED\_TRANSIENTDB

A list of Transient names is excluded from being stored in the Database.\
Default:

```php
define('DOCKET_CACHE_IGNORED_TRANSIENTDB',
    [
        'doing_cron',
        'update_plugins',
        'update_themes',
        'update_core',
    ]
);
```

## DOCKET\_CACHE\_CRONBOT

The Cronbot is an [external service](https://cronbot.docketcache.com/) that pings your website every hour to keep WordPress Cron running actively. Only site Timezone, URL and version are involved when enabling this service.

Set to `true` or `false` to enable or disable Cronbot Service.\
Default:

```php
define('DOCKET_CACHE_CRONBOT', false);
```

## DOCKET\_CACHE\_CRONBOT\_MAX

Maximum sites allowed in Multisite setup.\
Default:

```php
define('DOCKET_CACHE_CRONBOT_MAX', 10);
```

## DOCKET\_CACHE\_OPCVIEWER

Set to true or false to enable or disable the OPcache viewer feature.\
Default:

```php
define('DOCKET_CACHE_OPCVIEWER', false);
```

## DOCKET\_CACHE\_GCACTION

Set to `true` to enable Docket Cache Garbage Collector action button at Overview screen.\
Default:

```php
define('DOCKET_CACHE_GCACTION', false);
```

## DOCKET\_CACHE\_FLUSHACTION

Set to `true` to enable Docket Cache the additional Flush Cache action button on the Configuration screen.\
Default:

```php
define('DOCKET_CACHE_FLUSHACTION', false);
```

## DOCKET\_CACHE\_AUTOUPDATE

Set to `true` or `false` to force enable or disable automatic updates of the Docket Cache.\
Default:

```php
define('DOCKET_CACHE_AUTOUPDATE', true);
```

## DOCKET\_CACHE\_CHECKVERSION

The Check Version allows Docket Cache to check any critical future version that requires removing cache files before doing the updates, purposely to avoid error-prone.

Set to `true` or `false` to enable or disable critical version checking.\
Default:

```php
define('DOCKET_CACHE_CHECKVERSION', false);
```

## DOCKET\_CACHE\_FLUSH\_SHUTDOWN

Set to `true` or `false` to enable or disable to flush the object cache when disabling or uninstalling Docket Cache.\
Default:

```php
define('DOCKET_CACHE_FLUSH_SHUTDOWN', false);
```

## DOCKET\_CACHE\_OPCSHUTDOWN

Set to `true` or `false` to enable or disable to flush OPcache when disabling or uninstalling Docket Cache.

```php
define('DOCKET_CACHE_OPCSHUTDOWN', false);
```

## DOCKET\_CACHE\_STATS

Set to `true` or `false` to enable or disable object cache data stats at Overview screen.\
Default:

```php
define('DOCKET_CACHE_STATS', true);
```

## DOCKET\_CACHE\_PINGBACK

Set to true to disable WordPress XML-RPC and Pingbacks related features.\
Default:

```php
define('DOCKET_CACHE_PINGBACK', false);
```

## DOCKET\_CACHE\_HEADERJUNK

Set to true to disable WordPress features related to HTML header such as meta generators and feed links to reduce the page size.\
Default:

```php
define('DOCKET_CACHE_HEADERJUNK', false);
```

## DOCKET\_CACHE\_WPEMOJI

Set to true to disable WordPress Emoji feature.\
Default:

```php
define('DOCKET_CACHE_WPEMOJI', false);
```

## DOCKET\_CACHE\_WPFEED

Set to true to disable WordPress Feed feature.\
Default:

```php
define('DOCKET_CACHE_WPFEED', false);
```

## DOCKET\_CACHE\_WPEMBED

Set to true to disable WordPress Embed feature.\
Default:

```php
define('DOCKET_CACHE_WPEMBED', false);
```

## DOCKET\_CACHE\_WPLAZYLOAD

Set to true to disable WordPress Lazy Load feature.\
Default:

```php
define('DOCKET_CACHE_WPLAZYLOAD', false);
```

## DOCKET\_CACHE\_WPSITEMAP

Set to true to disable WordPress Auto-Sitemap feature.\
Default:

```php
define('DOCKET_CACHE_WPSITEMAP', false);
```

## DOCKET\_CACHE\_WPAPPPASSWORD

Set to true to disable WordPress Application Passwords feature.\
Default:

```php
define('DOCKET_CACHE_WPAPPPASSWORD', false);
```

## DOCKET\_CACHE\_WPDASHBOARDNEWS

Set to true to disable WordPress Events & News Feed at Dashboard.\
Default:

```php
define('DOCKET_CACHE_WPDASHBOARDNEWS', false);
```

## DOCKET\_CACHE\_WPBROWSEHAPPY

Set to true to disable the WordPress Browse Happy HTTP API requests, which checks whether the user needs a browser update.\
Default:

```php
define('DOCKET_CACHE_WPBROWSEHAPPY', false);
```

## DOCKET\_CACHE\_WPSERVEHAPPY

Set to true to disable the WordPress Serve Happy HTTP API request, which checks whether the user needs to update PHP.\
Default:

```php
define('DOCKET_CACHE_WPSERVEHAPPY', false);
```

## DOCKET\_CACHE\_POSTVIAEMAIL

Set to true to disable the WordPress post-via-email functionality.\
Default:

```php
define('DOCKET_CACHE_POSTVIAEMAIL', false);
```

## DOCKET\_CACHE\_LIMITHTTPREQUEST

Set to true to limit HTTP requests in WP-Admin.

This option will block any HTTP requests made by plugins or themes that used `wp_remote_post`, `wp_remote_get` or `wp_remote_request` functions that are not invoked in standard WP-Admin pages like Post, Pages, Plugins, Media and others.

Default:

```php
define('DOCKET_CACHE_LIMITHTTPREQUEST', false);
```

## DOCKET\_CACHE\_LIMITHTTPREQUEST\_WHITELIST

Set the list of hosts excluded from Limit HTTP requests options.\
Default:

```php
define('DOCKET_CACHE_LIMITHTTPREQUEST_WHITELIST', []);
```

#### Example:

```php
define('DOCKET_CACHE_LIMITHTTPREQUEST_WHITELIST', 
    [
        'feeds.feedburner.com',
        'api.docketcache.com'
    ]
);
```

## DOCKET\_CACHE\_GCRON\_DISABLED

Set to true to disable Garbage Collector Cron Events. By defining it as true, Docket Cache will not install Cron Event for Garbage Collector. You need to run it manually using wp-cli or a custom Cron Events.\
Default:

```php
define('DOCKET_CACHE_GCRON_DISABLED', false);
```

#### Example for WP-CLI:

```
wp cache run:gc
```

#### Example for custom Cron Events:

```php
<?php
// Place this code in wp-content/mu-plugins/docketcache-gcron.php
if ( !defined('DOCKET_CACHE_GCRON_DISABLED') || !DOCKET_CACHE_GCRON_DISABLED) {
    exit;
}

add_action('docketcache_custom_gcron', function() {
    if ( has_filter('docketcache/filter/garbagecollector') ) {
        $results = apply_filters('docketcache/filter/garbagecollector', true);
        if (!empty($results) && \is_object($results)) {
            if ($results->is_locked) {
                // Process is locked.
                return;
            }
            // Process is Ok.
        }
    }
});

if (!wp_next_scheduled('docketcache_custom_gcron')) {
    wp_schedule_event(time(), 'hourly', 'docketcache_custom_gcron');
}

```

## DOCKET\_CACHE\_DISABLED

Set to true to disable the Docket Cache object cache feature at runtime. By defining it as true, Docket Cache will ignore to install and uninstall the Drop-in file.\
Default:

```php
define('DOCKET_CACHE_DISABLED', false);
```


# WP-CLI

The command line interface for WordPress.

`Updated: 03-Mar-2023 | v22.07.04`

WP-CLI is the official command-line interface for WordPress. The Docket Cache extends the default `wp cache` command with additional sub-commands.

The following commands are supported. You may use `--verbose` on some commands to display more output.

## wp cache status

Display the Docket Cache status.

```shell
wp cache status
```

**Example output:**

```
---------------:--------------------------------
Cache Status   : Enabled
Cache Path     : /wp-content/cache/docket-cache
Cache Size     : 717K
---------------:--------------------------------
```

## wp cache dropin:enable

Enable the Docket Cache `object-cache.php` Drop-In file. The default behaviour is to create the object-cache.php Drop-In and replace any existing `object-cache.php` Drop-In.

```shell
wp cache dropin:enable
```

## wp cache dropin:disable

Disable the Docket Cache object-cache.php Drop-In file. The default behaviour is to delete the `object-cache.php` Drop-In unless an unknown `object-cache.php` Drop-In is present.

```shell
wp cache dropin:disable
```

## wp cache dropin:update

Update the Docket Cache `object-cache.php` Drop-In file. The default behaviour is to overwrite any existing `object-cache.php` Drop-In.

```shell
wp cache update
```

## wp cache flush

Remove the cache files.

```shell
wp cache flush
```

## wp cache flush:menucache

Remove the Menu cache files.

```
wp cache flush:menucache
```

## wp cache flush:mocache

Remove the Translation cache files.

```
wp cache flush:mocache
```

## wp cache flush:precache

Remove the Precache cache files.

```shell
wp cache flush:precache
```

## wp cache flush:transient

Remove the Transients cache files.

```
wp cache flush:transient
```

## wp cache flush:advcpost

Remove the Advanced Post Cache cache files.

{% hint style="info" %}
Since version 22.07.04, this command is only available for WordPress version 6.1 and below.
{% endhint %}

## wp cache reset:lock

Reset the Docket Cache lock files.

```shell
wp cache reset:lock
```

## wp cache reset:cron

Reset the Docket Cache cron event.

```shell
wp cache reset:cron
```

**Example output:**

```
Resetting cron event. Please wait..
+------------------------------------+---------------------+-----------------------+------------+
| hook                               | next_run_gmt        | next_run_relative     | recurrence |
+------------------------------------+---------------------+-----------------------+------------+
| docketcache_gc                     | 2020-12-08 16:58:36 | now                   | 5 minutes  |
| docketcache_watchproc              | 2020-12-08 16:58:36 | now                   | 1 hour     |
| docketcache_checkversion           | 2020-12-08 16:58:36 | now                   | 5 days     |
+------------------------------------+---------------------+-----------------------+------------+
Success: Cron event has been reset.

```

## wp cache run:gc

Run the Docket Cache garbage collector (GC).

```shell
wp cache run:gc
```

**Example output:**

```
Executing the garbage collector. Please wait..
-----------------------------------:----------
Cache MaxTTL                       : 345600
Cache File Limit                   : 50000
Cache Disk Limit                   : 500M
-----------------------------------:----------
Cleanup Cache MaxTTL               : 0
Cleanup Cache File Limit           : 0
Cleanup Cache Disk Limit           : 0
-----------------------------------:----------
Total Cache Cleanup                : 0
Total Cache Ignored                : 0
Total Cache File                   : 1580
-----------------------------------:----------
Success: Executing the garbage collector completed.
```

## wp cache run:cron

Run all cron event.

```shell
wp cache run:cron
```

**Example output:**

```
Executing the cron event. Please wait..
Executed the cron event 'docketcache_watchproc' in 0.011s.
Executed the cron event 'docketcache_gc' in 0.338s.
Executed the cron event 'wp_privacy_delete_old_export_files' in 0.011s.
Executed the cron event 'wp_version_check' in 6.804s.
Executed the cron event 'wp_update_plugins' in 2.817s.
Executed the cron event 'wp_update_themes' in 0.011s.
Executed the cron event 'recovery_mode_clean_expired_keys' in 0.005s.
Executed the cron event 'wp_scheduled_delete' in 0.005s.
Executed the cron event 'delete_expired_transients' in 0.005s.
Executed the cron event 'wp_scheduled_auto_draft_delete' in 0.007s.
Executed the cron event 'docketcache_checkversion' in 0.012s.
Executed the cron event 'wp_site_health_scheduled_check' in 0.091s.
Success: Executed a total of 12 cron events.
```

## wp cache run:optimizedb

Runs the Docket Cache Optimizedb.

```
wp cache run:optimizedb
```

## wp cache run:stats

Run the Docket Cache stats function to collect cache data.

```shell
wp cache run:stats
```

**Example output:**

```
Executing the cache stats. Please wait..
---------------:----------                                                                          
Object size    : 885K
File size      : 992K
Total file     : 66
---------------:----------
Success: Executing the cache stats completed.
```

## wp cache runtime:install

Install the Docket Cache runtime code.

```
wp cache runtime:install
```

## wp cache runtime:remove

Removes the Docket Cache runtime code.

```
wp cache runtime:remove
```


# Admin Interface

Docket Cache WP Admin Interface

`Updated: 10-Mar-2023 | v22.07.04`

The Docket Cache keeps the admin interface clean, responsive and as simple as possible, with predefined configurations and reusing WordPress libraries as much as possible.

## Overview

The Overview screen is the primary place to view the current status of Docket Cache activity, configuration, and other useful information.

| Label                         | Description                                                           |
| ----------------------------- | --------------------------------------------------------------------- |
| **Web Server**                | Web Server name.                                                      |
| **PHP SAPI**                  | PHP version and type of Server API.                                   |
| **Cloudflare**                | Cloudflare IP and Ray ID. (1)                                         |
| **Web Proxy**                 | Web Proxy IP other than Cloudflare. (2)                               |
| **Object Cache Stats**        | Total object size in cache files.                                     |
| **Object OPcache Stats**      | Total OPcache size in memory, for objects cache files.                |
| **WP OPcache Stats**          | Total OPcache size in memory, for WordPress files.                    |
| **PHP Memory Limit**          | Your Server PHP memory limit setting.                                 |
| **WP Frontend Memory Limit**  | WordPress Website memory limit.                                       |
| **WP Backend Memory Limit**   | WordPress Admin memory limit.                                         |
| **WP Multi Site**             | Status either is Multisite. (3)                                       |
| **WP Multi Network**          | Status either is Multi-Network. (4)                                   |
| **Primary Network**           | Status either is Primary Network. (4)                                 |
| **Network Locking File**      | Network Lock file. (4)                                                |
| **Drop-in Writable**          | Status either Drop-in file can be written, replace or delete.         |
| **Drop-in use Wrapper**       | Status either Drop-in file is wrapper file. (5)                       |
| **Drop-in Wrapper Available** | Status either Drop-in wrapper file exists. (5)                        |
| **Drop-in Wrapper File**      | Drop-in wrapper file location. (5)                                    |
| **Drop-in File**              | Drop-in file path.                                                    |
| **Cache Writable**            | Status either cache file can be written, replace or delete.           |
| **Cache Files Limit**         | Current total cache files and maximum files can be store on disk.     |
| **Cache Disk Limit**          | Current total size cache files and maximum size can be store on disk. |
| **Cache Path**                | Cache directory path.                                                 |
| **Config Writable**           | Status either config file can be written, replace or delete.          |
| **Config Path**               | Config directory path.                                                |

{% hint style="info" %}

1. Only visible if your website running behind Cloudflare.
2. Only visible if web proxy is not Cloudflare such Sucuri and Varnish.
3. Only visible in Multisite single-network.
4. Only visible in Multisite Multi-Network setup.
5. Only visible if `DOCKET_CACHE_CONTENT_PATH` constant defined.
   {% endhint %}

## Configuration

The configuration screen allows you to change the Docket Cache behaviour without using constant variables. If related constants are defined in the `wp-config.php` file, it will overwrite the changes on this screen.

#### FEATURE OPTIONS

| Label               | Related Constant                                                                      |
| ------------------- | ------------------------------------------------------------------------------------- |
| **Cronbot Service** | [DOCKET\_CACHE\_CRONBOT](https://docs.docketcache.com/constants#docket_cache_cronbot) |
| **OPcache Viewer**  | [DOCKET\_CACHE\_OPCVIEWER](#overview)                                                 |
| **Cache Log**       | [DOCKET\_CACHE\_LOG](https://docs.docketcache.com/constants#docket_cache_log)         |

#### CACHE OPTIONS

| Label                             | Related Constant                                                                              |
| --------------------------------- | --------------------------------------------------------------------------------------------- |
| **Advanced Post Caching**         | [DOCKET\_CACHE\_ADVCPOST](https://docs.docketcache.com/constants#docket_cache_advcpost)       |
| **Object Cache Precaching**       | [DOCKET\_CACHE\_PRECACHE](https://docs.docketcache.com/constants#docket_cache_precache)       |
| **WordPress Translation Caching** | [DOCKET\_CACHE\_MOCACHE](https://docs.docketcache.com/constants#docket_cache_mocache)         |
| **Admin Page Cache Preloading**   | [DOCKET\_CACHE\_PRELOAD](https://docs.docketcache.com/constants#docket_cache_preload)         |
| **Retain Transients in Db**       | [DOCKET\_CACHE\_TRANSIENTDB](https://docs.docketcache.com/constants#docket_cache_transientdb) |

#### OPTIMISATIONS

| Label                           | Related Constant                                                                                            |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| **Optimize WP Query**           | [DOCKET\_CACHE\_OPTWPQUERY](https://docs.docketcache.com/constants#docket_cache_optwpquery)                 |
| **Optimize Term Count Queries** | [DOCKET\_CACHE\_OPTERMCOUNT](https://docs.docketcache.com/constants#docket_cache_optermcount)               |
| **Optimize Database Tables**    | [DOCKET\_CACHE\_CRONOPTMZDB](https://docs.docketcache.com/constants#docket_cache_cronoptmzdb)               |
| **Suspend WP Options Autoload** | [DOCKET\_CACHE\_WPOPTALOAD](https://docs.docketcache.com/constants#docket_cache_wpoptaload)                 |
| **Post Missed Schedule Tweaks** | [DOCKET\_CACHE\_POSTMISSEDSCHEDULE](https://docs.docketcache.com/constants#docket_cache_postmissedschedule) |
| **Limit Bulk Edit Actions**     | [DOCKET\_CACHE\_LIMITBULKEDIT](https://docs.docketcache.com/constants#docket_cache_limitbulkedit)           |
| **Misc Performance Tweaks**     | [DOCKET\_CACHE\_MISC\_TWEAKS](https://docs.docketcache.com/constants#docket_cache_misc_tweaks)              |

#### WOO TWEAKS

| Label                                         | Related Constant                                                                                                  |
| --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| **Misc WooCommerce Tweaks**                   | [DOCKET\_CACHE\_WOOTWEAKS](https://docs.docketcache.com/constants#docket_cache_wootweaks)                         |
| **Deactivate WooCommerce Admin**              | [DOCKET\_CACHE\_WOOADMINOFF](https://docs.docketcache.com/constants#docket_cache_wooadminoff)                     |
| **Deactivate WooCommerce Classic Widget**     | [DOCKET\_CACHE\_WOOWIDGETOFF](https://docs.docketcache.com/constants#docket_cache_woowidgetoff)                   |
| **Deactivate WooCommerce WP Dashboard**       | [DOCKET\_CACHE\_WOOWPDASHBOARDOFF](https://docs.docketcache.com/constants#docket_cache_woowpdashboardoff)         |
| **Deactivate WooCommerce Extensions Page**    | [DOCKET\_CACHE\_WOOEXTENSIONPAGEOFF](https://docs.docketcache.com/constants#docket_cache_wooextensionpageoff)     |
| **Deactivate WooCommerce Cart Fragments**     | [DOCKET\_CACHE\_WOOCARTFRAGSOFF](https://docs.docketcache.com/constants#docket_cache_woocartfragsoff)             |
| **Prevent robots crawling add-to-cart links** | [DOCKET\_CACHE\_WOOADDTOCHARTCRAWLING](https://docs.docketcache.com/constants#docket_cache_wooaddtochartcrawling) |

#### WP TWEAKS

| Label                                          | Related Constant                                                                                        |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| **Remove XML-RPC / Pingbacks**                 | [DOCKET\_CACHE\_PINGBACK](https://docs.docketcache.com/constants#docket_cache_pingback)                 |
| **Remove WP Header Junk**                      | [DOCKET\_CACHE\_HEADERJUNK](https://docs.docketcache.com/constants#docket_cache_headerjunk)             |
| **Deactivate WP Emoji**                        | [DOCKET\_CACHE\_WPEMOJI](https://docs.docketcache.com/constants#docket_cache_wpemoji)                   |
| **Deactivate WP Feed**                         | [DOCKET\_CACHE\_WPFEED](https://docs.docketcache.com/constants#docket_cache_wpfeed)                     |
| **Deactivate WP Embed**                        | [DOCKET\_CACHE\_WPEMBED](https://docs.docketcache.com/constants#docket_cache_wpembed)                   |
| **Deactivate WP Lazy Load**                    | [DOCKET\_CACHE\_WPLAZYLOAD](https://docs.docketcache.com/constants#docket_cache_wplazyload)             |
| **Deactivate WP Sitemap**                      | [DOCKET\_CACHE\_WPSITEMAP](https://docs.docketcache.com/constants#docket_cache_wpsitemap)               |
| **Deactivate WP Application Passwords**        | [DOCKET\_CACHE\_WPAPPPASSWORD](https://docs.docketcache.com/constants#docket_cache_wpapppassword)       |
| **Deactivate WP Events & News Feed Dashboard** | [DOCKET\_CACHE\_WPDASHBOARDNEWS](https://docs.docketcache.com/constants#docket_cache_wpdashboardnews)   |
| **Deactivate Post Via Email**                  | [DOCKET\_CACHE\_POSTVIAEMAIL](https://docs.docketcache.com/constants#docket_cache_postviaemail)         |
| **Deactivate Browse Happy Checking**           | [DOCKET\_CACHE\_WPBROWSEHAPPY](https://docs.docketcache.com/constants#docket_cache_wpbrowsehappy)       |
| **Deactivate Serve Happy Checking**            | [DOCKET\_CACHE\_WPSERVEHAPPY](https://docs.docketcache.com/constants#docket_cache_wpservehappy)         |
| **Limit WP-Admin HTTP Requests**               | [DOCKET\_CACHE\_LIMITHTTPREQUEST](https://docs.docketcache.com/constants#docket_cache_limithttprequest) |

#### STORAGE OPTIONS

| Label                             | Related Constant                                                                                           |
| --------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| **Cache Files Limit**             | [DOCKET\_CACHE\_MAXFILE](https://docs.docketcache.com/constants#docket_cache_maxfile)                      |
| **Cache Disk Limit**              | [DOCKET\_CACHE\_MAXSIZE\_DISK](https://docs.docketcache.com/constants#docket_cache_maxsize_disk)           |
| **Chunk Cache Directory**         | [DOCKET\_CACHE\_CHUNKCACHEDIR](https://docs.docketcache.com/constants#docket_cache_chunkcachedir)          |
| **Real-time File Limit Checking** | [DOCKET\_CACHE\_MAXFILE\_LIVECHECK](https://docs.docketcache.com/constants#docket_cache_maxfile_livecheck) |
| **Auto Remove Stale Cache**       | [DOCKET\_CACHE\_FLUSH\_STALECACHE](https://docs.docketcache.com/constants#docket_cache_flush_stalecache)   |
| **Exclude Empty Object Data**     | [DOCKET\_CACHE\_EMPTYCACHE\_IGNORE](https://docs.docketcache.com/constants#docket_cache_emptycache_ignore) |

#### ADMIN INTERFACE

| Label                                    | Related Constant                                                                              |
| ---------------------------------------- | --------------------------------------------------------------------------------------------- |
| **Admin Page Loader**                    | [DOCKET\_CACHE\_PAGELOADER](https://docs.docketcache.com/constants#docket_cache_pageloader)   |
| **Object Cache Data Stats**              | [DOCKET\_CACHE\_STATS](https://docs.docketcache.com/constants#docket_cache_stats)             |
| **Garbage Collector Action Button**      | [DOCKET\_CACHE\_GCACTION](https://docs.docketcache.com/constants#docket_cache_gcaction)       |
| **Additional Flush Cache Action Button** | [DOCKET\_CACHE\_FLUSHACTION](https://docs.docketcache.com/constants#docket_cache_flushaction) |

#### PLUGIN OPTIONS

| Label                                      | Related Constant                                                                                     |
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------- |
| **Check Critical Version**                 | [DOCKET\_CACHE\_CHECKVERSION](https://docs.docketcache.com/constants#docket_cache_checkversion)      |
| **Flush Object Cache During Deactivation** | [DOCKET\_CACHE\_FLUSH\_SHUTDOWN](https://docs.docketcache.com/constants#docket_cache_flush_shutdown) |
| **Flush OPcache During Deactivation**      | [DOCKET\_CACHE\_OPCSHUTDOWN](https://docs.docketcache.com/constants#docket_cache_opcshutdown)        |

## Cache Log

The cache log screen allows you to view the cache log for debugging and monitor cache activities. This screen is only visible if the Cache Log option is enabled on the configuration screen.

| Label         | Description                |
| ------------- | -------------------------- |
| **Timestamp** | Timestamp format.          |
| **Log All**   | Enable or Disable Log All. |
| **Log File**  | Log file path.             |
| **Log Size**  | Log file size.             |
| **Flush Log** | Flush log file.            |

{% hint style="info" %}
Please refer to [`DOCKET_CACHE_LOG*`](https://docs.docketcache.com/constants#docket_cache_log) related constant for details.
{% endhint %}

## Cronbot

The cronbot screen allows you to connect Docket Cache with Cronbot Service. This screen also provides a function to view and execute registered cron tasks.

| Label                   | Description                                                      |
| ----------------------- | ---------------------------------------------------------------- |
| **Service Status**      | Status either connected to Cronbot Service.                      |
| **Last Received Ping**  | Timestamp last Cronbot Service connect to your website.          |
| **Next Expecting Ping** | Timestamp next Cronbot Service expected connect to your website. |
| **Connect**             | Connect to Cronbot Service.                                      |
| **Disconnect**          | Disconnect from Cronbot Service.                                 |
| **Run Scheduled Event** | Execute scheduled cron task.                                     |
| **Run All Now**         | Execute all cron task.                                           |

{% hint style="info" %}
Please refer to [`DOCKET_CACHE_CRONBOT`](https://docs.docketcache.com/constants#docket_cache_cronbot) constant for details.
{% endhint %}


# FAQ

Frequently Asked Questions

## What is Object Caching in WordPress?

Object caching is a process that stores database query results in order to quickly bring them back up next time they are needed.

The cached object will be served promptly from the cache rather than sending multiple requests to a database. This is more efficient and reduces massive unnecessary loads on your server.

In simple terms, object caching allows objects that are used often to be copied and stored at a closer location for quicker use.

## What is Docket Cache in Object Caching?

By default, the object cache in WordPress is non-persistent. This means that data stored in the cache reside in memory only and only for the duration of the request. Cached data will not be stored persistently across page loads. To make it persistent, the object cache must be stored on a local disk.

Docket Cache is not just stored the object cache, it converts the object cache into plain PHP code. This solution is faster since WordPress can use the cache directly without running other operation.

## What is the Cronbot Service in Docket Cache?

The Cronbot is an external service that pings your website every hour to keep WordPress Cron running actively.

This service offered as an alternative option and is not compulsory to use. By default, this service not connected to the [end-point server](https://cronbot.docketcache.com/). You can completely disable it at the configuration page.

## What is Garbage Collector in Docket Cache?

Garbage Collector is a Cron Event that runs every 5 minutes to monitor cache files purposely for cleanup and collecting stats.

## What is OPcache in Docket Cache?

OPcache is a caching engine built into PHP, that improves performance by storing precompiled script bytecode in shared memory, thereby removing the need for PHP to load and parse scripts on each request.

Docket Cache converts the object cache into plain PHP code. When reading and writing cache, it will use OPcache directly which results in faster data retrieval and better performance.

## What is a RAM disk in Docket Cache?

A RAM disk is a representation of a hard disk using RAM resources, and it can take the form of a hardware device or a virtual disk.

Read and write speed on RAM is multiple times faster than SSD drives therefore storing Docket Cache files on a RAM disk greatly increases it's performance.

Do note that creating RAM disks requires server administrative permission (root access) so this solution is not suitable for shared hosting servers.

This is an example command to create and use a RAM disk with Docket Cache:

```
$ cd wp-content/
$ sudo mount -t tmpfs -o size=500m tmpfs ./cache/docket-cache
```

Kindly refer to the articles below about RAM disk:

1. [How to Easily Create RAM Disk](https://www.linuxbabe.com/command-line/create-ramdisk-linux)
2. [What Is /dev/shm And Its Practical Usage](https://www.cyberciti.biz/tips/what-is-devshm-and-its-practical-usage.html)
3. [Creating A Filesystem In RAM](https://www.cyberciti.biz/faq/howto-create-linux-ram-disk-filesystem/)

To use it in Windows OS, create RAM Disk and change [DOCKET\_CACHE\_PATH](https://docs.docketcache.com/configuration#docket_cache_path) point to RAM Disk drive.

## What is the minimum RAM required to use with shared hosting?

By default, WordPress allocates the memory limit to 256 MB. Combined with MySQL and Web Server, you need more than 256 MB. If you're using a cheap hosting plan that allocates only 256 MB for totals usage. It is not enough, and Docket Cache can't improve your website performance.

## What’s the difference with the other object cache plugin?

Docket Cache is an Object Cache Accelerator. It does some optimisation like cache post queries, comments counting, WordPress translation and more before storing the object caches.

## Can I pair using it with other cache plugins?

Yes and No. You can pair using it with page caching plugin, but not with the object cache plugin.

## Can I pair using it with LiteSpeed Cache?

Yes, you can. The LiteSpeed Cache plugin has an Object Cache feature. Currently, by default, it will prompt a notice asking to disable Docket Cache. You only need to turn off LiteSpeed Cache Object Cache in order to use Docket Cache.

## Can I use Docket Cache on heavy WooCommerce stores?

Yes and No. As suggested, Docket Cache is an alternative to in-memory caches like Redis and Memcached. It depends on how your store has been setups. It may require further tuning to the configuration and may involve other optimisations.

## I'm using a VPS server. Can I use Docket Cache to replace Redis?

Yes, you can. It can boost more your WordPress performance since there is no network connection need to makes and no worry about memory burst, cache-key conflict and error-prone caused by the improper settings.


# WordPress Hosting

Understanding the different types of hosting for WordPress.

Putting a WordPress website online means having hosting. There are numerous types of hosting to consider, each of which has its benefits, pitfalls and target groups.

{% hint style="success" %}
This article originally from [Andrew Killen Note](https://www.facebook.com/notes/andrew-killen/understanding-the-different-types-of-hosting-for-wordpress/10155584723456701/).
{% endhint %}

## Free Hosted Service

wordpress.com is a free platform that is maintained and owned by Automattic. Automattic is owned by Matt Mullenweg, who is the benevolent dictator for life of WordPress. Hosting on WordPress.com’s platform is very easy to setup, but is limited in the plugins & themes you can use, and force your site to carry advertising in order to pay for this service.\
\
Due to WordPress’s multisite capability, others are offering this service, but I’m sticking to WordPress.com for this overview.

#### Pro's

* Free.
* Good platform.
* Can use your own domain name.

#### Con's

* A limited number of themes and plugins.
* Forced advertising on site.
* Automattic can pull the plug on your website without warning.
* Things like backups and restores are not feasible.
* Limited to community support and 3GB max data space.

#### Target Audience

People who just want to start blogging.

## Cheap Shared hosting

I am sure you have seen it, $1 per month hosting or similar, maybe promising unlimited disk space, unlimited bandwidth, unlimited email accounts. The only way that this can be achieved is by 2 things.

1. Putting as many websites as possible on one server (we’re talking thousands).
2. Offering very poor support.

By having many thousands of websites on one server, there will always be contention for processing power, disk access, and database calls. You can be sure that the site will at some point start to run slow, maybe be out of the water for days on end.

Mostly the host will not care. Offering the hosting as such a cheap means that they will not have a lot of money left over to pay for qualified staff to manage and maintain their systems. If the server costs 80 cents off of the dollar per month to keep running, then they can only make a profit on the 20 cents.

Take in to account the cost of the financial transaction and offices, they maybe down to as low as 5 cents per month for support. So don’t expect the best of the best technologist, quick answer times on the phone and chat. Also don’t expect the latest and greatest to be installed on the server, i.e. latest version of PHP. They just can’t afford to do it for you. I am assured by the people at Hostinger that this is not always the case, and they specialize in this part of the marketplace.

#### Pro's

* Super cheap.
* You can have the themes and plugins of your choice.
* An admin panel where you can administer your website(s).
* Possible SSL via LetsEncrypt for free (not guaranteed).

#### Con's

* May not be the latest software or hardware.
* High chance of hackers breaking in from one of the other sites hosted on the server.
* A Server might often run slow.
* Support might be hard to get.
* A Server might be out of action for days at a time.
* Every problem will be resolved with an upsell to better hosting.
* Definitely not suitable for e-commerce.

#### Target audience

Those that do not value their business enough to invest in it, or just need a very basic web presence.

#### Recommendation

Don’t do this if you are in any way professional about your business or online presence. Check how others have found your hosting company of choice before signing up.

## Professional Shared Hosting

Professional shared hosting usually starts at around $20 per month, and like cheap shared hosting makes use of putting many websites on one server. However, that is where the similarity ends. Companies like siteground, WPengine, Flywheel, WordPress.com, to name but a few target this part of the marketplace (a more comprehensive list [here](https://reviewsignal.com/webhosting#tab6)).

Usually, they offer a very structured hosting package, where there are limits per month for what bandwidth and processing can be used so that they can be sure that the server will not be overworked. Also, they will often have limits on what plugins can be installed.

This is based on, for example, either not needing the caching plugin to be installed as their infrastructure takes care of it, or that they know a certain plugin uses far too much processing power or has a high chance of the site being hacked if it is used. If you do get problems and need support, you can be sure in this package that they will be prompt in answering, and will less often offer up-sell as a way to resolve problems.

#### Pro's

* Usually free SSL cert.
* Often free domain name in the package.
* Quality support staff.
* Good administration tools.

#### Con's

* Limited resources are available.
* Sometimes upsell will be offered as a fix resolution.
* Possible that the website will be out of action if you have a viral post and you have not worked with the hosting company to deal with the extra processing/bandwidth resolution.
* Might not have SSH access.

#### Target Audience

Those that value their online presence. This is a suitable business solution for the lower tier.

#### Recommendation

This is a good first start for a website platform. Consider the benefits of your chosen company before buying, for example, WPEngine pride themselves on their security so are good for E-commerce, Flywheel is aimed at developers and making their life easier (good tools and staging), Siteground do well with caching and hosting in general, few complain about them.

## Self Managed VPS (Virtual Private Server)

A VPS is a server that you own and can put on there what you like. Usually, they are based on top of Windows or Linux, for WordPress it’s always better to go the Linux route. Usually, when buying a VPS, you pay for the number of processors you use and the amount of memory wanted, lastly the amount of disk space needed.

There are many companies that are working in this space, some of the more popular ones are Linode, [Digital Ocean](https://m.do.co/c/6c93db5b1ef6), Vultr Amazon AWS, MS Azure etc.. And start as low as $5 per month. The downside to this type of hosting is that you own the machine completely if it breaks it’s down to you, you manage the security, the operating system, the server configuration, email configuration and so on. If you are not a DevOps person, do not take this route.

#### Pro's

* Cheap.
* Versatile.
* Can install whatever you want.
* Your choice of the web server.
* Extremely fast.
* You can choose where in the world the server is.

#### Con's

* You own it, manage it, maintain it, secure it.
* Ownership takes time.

#### Target Audience

Those that are as happy with Linux as they are with PHP, Varnish, NginX/Apache, REDIS, Memcache or PostFix. If any of those words scare you, do not choose this option.

#### Recommendation

These are great hosting for test sites, and for those that really are on top of their technology stack. Remember that if you own a large site, you will also need staff to support it. AWS & Azure are more difficult than other hosting packages to understand exactly what it is you will need and pay for, consider employing a consultant to help define this, RackSpace are very experienced at this.

## Managed VPS

There are a growing number of companies that manage the VPS technology for you. They do this by installing a stack that they own and know intimately and can support fully. Perhaps the best-known name in this arena at the moment is cloudways, where they offer many different platforms for hosting, with same technology stack on each.

So you can choose if you want Digital Ocean or AWS, and leave them to do the rest. If you need to be installing a specific technology stack, this is not for you. But if you are just looking for quality hosting at reasonable prices then this is a great option. Be aware that if you choose this path, you might not get such things as SSH access.

#### Pro's

* Great value for money.
* Can grow to meet your bandwidth needs instantly.
* You do not support it.
* Proven stack that works.
* Security is managed by someone else.

#### Con's

* Possibly no SSH access.
* Cannot install whatever you want (i.e. Fail2Ban).
* You have to have a bit of web hosting savvy when making the purchase to speck out the right system.

#### Target Audience

Those that want a professional level of hosting that can grow without worry and will not have to employ own support staff to manage it. If you just want a WordPress website online and don’t care about the underlying technology, this is one of the most cost-effective solutions.

#### Recommendation

If you are doing anything special, before choosing this solution make sure that your technology stack will work and you do not need things that the managed VPS hosting company can’t deliver.

## Managed Hosting

We are now entering the upper tiers of hosting. This is the choice of the professional, large business, or those that want to be always online with high availability and strong security. There are many companies that focus on this arena, WPEngine, Kinsta, Pagely, Flywheel, Pantheon are some of them.

Like the Managed VPS, this will be a closely controlled environment that you might not get full access to. That’s ok because you pay the big bucks to get them to manage it for you. Together with the hosting company, you will define the number of users, database space and file space needed, they will take care of the rest. The only thing to check with these companies is the opening hours for support.

If your not in their timezone, then you will have problems getting the support you need as they will be closed.

#### Pro's

* Outrageously fast.
* Closely controlled stack.
* Strong Security.
* CDN often included in the deal.
* Some can do more than just WordPress.

#### Con's

* Maybe no SSH / WP-CLI.
* Support hours may not be 24/7.
* Packages limit the number of websites that can be run.
* Account management might only be on US hours.

#### Target Audience

This is the realm of the large business, a digital agency that resells sites or enterprise client. It's a comprehensive solution backed by high-quality support.

#### Recommendation

If you can, this is the area to aim for if you already have a successful online presence. However, before you buy, properly understand your current usage so that you can buy the right deal for you. If you have a website in your portfolio that has only a few pages and few visitors, this is a very expensive solution for it, consider putting small sites on other hosting and keeping this for the premium sites.

## Dedicated Server Hosting

You have a whole server to yourself when you buy a dedicated server. There are 2 options here, managed or unmanaged. Unless you have a really specific reason, choose managed.

If you choose this route, really consider a good conversation with the hosting company to define the type of machine you need or risk quick upgrades and unexpected costs. It is argued by many that the need for a totally dedicated server is not needed with server virtualization techniques, and a dedicated virtual server is just as good.

#### Pro's

* Blistering speed.
* No contention for processing, disk or memory.
* Good support.
* As many websites as you want.

#### Con's

* Might be outside the budget that you want to spend.
* Upgrading to a new server can be costly and time-consuming.

#### Target audience

Businesses that demand fast performance of their hardware. Those that are serious about e-commerce and do not want to run any risk of another site is being on their server. Again the higher tier customer wants this.

#### Recommendations

Like the managed VPS, this hosting company can be anywhere in the world, so consider choosing one that is close by your customer base to reduce the round-trip when getting data. And remember to choose one that has support hours in your prime-time.

## Dedicated Multi-server infrastructure

Once your hosting needs reach real scale than having a dedicated multi-server infrastructure is vital. This means Load Balancers to split the traffic over multiple caching varnish server, which in turn connect to multiple web server, who connect to a matrix of self duplicating database servers with a dedicated write server for your editors and many databases read servers for your site visitors.

Add on top all the Memcached, Redis and shared file storage you can think of, and you have a proper server infrastructure that can handle \*very\* high user numbers. Depending on the hosting company you work with, in this type of situation, it will be the hosting company that will do all the setup and maintenance of the infrastructure, where your DevOps people will need to be clever with the configuration and setup of the application (WordPress) you run. Others might just do everything for you. Expect to start around $40k per year for this kind of infrastructure with support.

#### Pro's

* You will be able to have as many websites as you want on here.
* Support will resolve tickets as soon as possible.
* Your resilient system will handle whatever visitors numbers you need.

#### Con's

* You pay by the GB for storage of files and database.
* Each part of your infrastructure will come with a price.
* SLA's (Service Level Agreements) can become very pricey.

#### Target Audience

Those that have traffic in the multi-millions of visitors per month, who want great support and a large amount of flexibility.

#### Recommendations

Really consider a hosting company that is close enough to visit. You are running a premium stack and want to be able to knock on their door if you are not getting the support or performance you need.

## Co-location Hosting

Your kit in someone else's computer room. OK, you'll have to buy a rack-based server and be aware that if it has a hardware or software failure you are the one to fix it.

#### Pro's

* You know the kit intimately.
* You're able to upgrade your hardware as needed.

#### Con's

* No server hardware support
* No software support
* You own upgrades and patching

#### Target Audience

Those that used to have a computer room in their building but do not want that responsibility or cost any more.

#### Recommendations

Research highly your choice of location, pay special attention to security, continuity, closeness to your company, and the peering point of the hosting location you choose.

## Redundant Failover Hosting

This could be with physical or virtual servers, that exist to jump into action when needed. The point being that if some catastrophic failure happens (think floods, power outage, EMP pulse, plane crashes into the building), then your money-making enterprise needs to continue.

Usually, this is enough distance away from the main installation that it is felt to be safe. Depending on your disaster recovery plan, this could be in the next-door building, 20KM down the road, or on the other side of the world. The main thing is that there will need to be significant connectivity between the two sites to allow for your database and files to be replicated on a regular basis.

#### Pro's

* Your business will not fail if the power cuts out or any other disaster.

#### Con's

* Really very expensive.
* Regular testing of the failover needed.

#### Target Audience

Those that cannot afford even 5 mins without their website online.

#### Recommendations

Weigh up if you need a ‘like for like’ failover or something that can run in a reduced capacity, to reduce your total costs. Also, have a plan for the human aspects of failover. Do your engineers need to fly to where the servers are? Do you need to tell your editors to stop working on the system so that write traffic is reduced? etc etc.


# Caching In WordPress

Core Caching Concepts in WordPress.

The purpose of this article is to provide a framework for thinking about caching. It discusses core caching concepts that can be difficult to grasp when first working with caching in web development projects. These concepts are then specifically applied to the WordPress context. Finally, some general tips are given about caching.

{% hint style="success" %}
This article originally from [tollmanz.com](https://www.tollmanz.com/core-caching-concepts-in-wordpress/) blog.
{% endhint %}

## Cache Types

To begin, this section will cover four different types of caches that one may encounter. An understanding of these concepts helps to navigate some of the jargon used when reading about caching systems.

### 1. Run-time Cache

A run time cache is a cache that only lasts the duration of a request. Objects are stored in memory but expelled as soon as the request is completed. Any time that you set a value or the results of a routine to variable and use it multiple times, you are making use of a run-time cache. If you need the same data twice in one request, there is no point in regenerating the data multiple times.

As a WordPress example, the main query and the current post object are stored in the `$wp_query` and `$post` global variables, respectively. When data about the current post is needed, MySQL isn’t queried again; rather, the data is pulled from the `$post` global variable. The run-time cache is a simple and efficient strategy for caching data.

The major problem with the run-time cache is that it only lasts the duration of the request. As soon as the request is completed, the cache is dumped. Even though a visitor would generate data that might be used across multiple requests, that data does not persist across requests and will need to be regenerated for every single request. This problem can be solved with object caching.

### 2. Object Caching

Object caching is the act of moving data from a place of expensive and slow retrieval to a place of cheap and fast retrieval. An object cache is also typically persistent, meaning that data cached during one request is available during subsequent requests.

In addition to making data access much easier, cached data should always be replaceable and regenerable. If an application experiences database corruption (e.g., MySQL, Postgres, Couchbase), there will and should be severe consequences for this database (and let us hope that there is a good backup plan in place). In contrast with the main data store for the application, if a cache is corrupted, the application should continue to function as the cached data should regenerate itself. No data will be lost, although there will likely be some performance problems as the cache regenerates.

The storage engine for an object cache can be a number of technologies. Popular object caching engines include Memcached, Redis, OPcache and the file system. The caching engine used should be dictated by the needs of the application. Each has its advantages and disadvantages. At a bare minimum, the engine used should make accessing the data more performant than regenerating the data.

The object cache tends to be very critical for the application because it can be used to implement the other caches that will be discussed in this article. In other words, if your object cache is implemented incorrectly, you may undermine the rest of your caching architecture.

### 3. Page Cache

A page cache stores HTML data that represents a single page. In many cases, the page uses the object cache to store its data. In such cases, a page cache is simply a special type of object cache. That said, the page cache can use an entirely different storage engine than the object cache. In fact, two popular choices for a page cache are Varnish and Nginx, which are a reverse proxy implementation of a page cache that stores data separately from the object cache.

Unlike the object cache engine that could be used for the page cache storage, the reverse proxy caching would not be good candidates for object caching storage engines and there are also some major technical limitations for using a reverse proxy cache for an object cache.

It is important to distinguish object and page caches. Page caches can lead to significant performance boosts for a web site with a minimal amount of effort; however, they are limited in that many page caching systems make the assumption that every page is rendered identically for every visitor. In other words, the assumption is often made that the page is never unique for an individual user. If your site meets this requirement, you will experience significant gains from implementing a page cache.

Page caching becomes extremely tricky and nearly impossible when the need for unique page views is introduced. In the case of unique pages for every visitor, effective use of object caching is crucial, but you likely will not see the same gains from only object caching that you will see from the only page cache.

It is always important to remember that, with some exceptions, when you implement a page cache, every user will see the same page. If you develop your site with data that is rendered uniquely for an individual (e.g., printing the user’s name in the header), that data will be cached for all users. There are certainly ways around this (e.g., do not cache logged in views) and you must consider this when implementing a page cache.

### 4. Fragment Caching

Fragment caching is the act of caching only part of a full page. Fragments are merely objects that are not full pages. It can be really tough to distinguish objects from fragments. When people talk about fragments, they are usually referring to identifiable chunks of a page. For example, a profile widget, footer, or related posts listing would all be considered fragments (but, ugh, they are also objects).

Typically, the fragment cache uses the object cache as the storage engine for the fragments. In that sense, a fragment cache is usually nothing more than an object cache that is storing named parts of a page.

## A Caching Metaphor

As the primary purpose of this article is to make sense of the caching concepts presented above, a metaphor will use to enhance the understanding of these concepts with particular emphasis on the page and object caching. Previously stated that we all have experience with caching and the concepts were presented. Let go too deep explanation.

Caching is like buying and storing groceries. When you go to the store, you purchase a variety of items. After returning from the store, you store items in your cabinets, refrigerator and counter. Your tip to the store is an act of caching. Obtaining food from one location and storing it in a new location that allows for cheaper and faster access follows the same principle. Let us compare a specific food item to caching.

Many of us buy eggs for use in different meals. One strategy for purchasing eggs would be to go to a market that sells them individually. If you wake up in the morning and want a 2 egg omelette, you could walk to the market, buy 2 eggs, return home and make the omelette. If you want an omelette for the next’s morning breakfast, you can repeat the process. Most of you will see this as a rather absurd process and will instantly see a more efficient strategy of buying a dozen eggs during a single trip to the market, then storing them in the refrigerator for quick access when making your morning meals.

The process of buying eggs and storing them in your refrigerator is similar to caching objects in web development. The process is similar in that you are moving a resource from a place of difficult access to a place of easy access.

To further improve on this metaphor, one purpose of going to the store is to obtain ingredients to make a meal. A meal is composed of numerous ingredients. If you have the ingredients in your refrigerator or cupboards, you can access those ingredients to compose the meal. The meal, in this case, is analogous to the page cache. The page cache is composed of many components, some of which are cached items. With making a meal, you pull items from your refrigerator or cabinets and you must visit the store if you are missing some items. The meal is then composed of items found in your house and the store. If you are really efficient, then the meal is composed entirely of items found in your house. This is similar to page caching in that if your application takes advantage of an object cache, your page cache can be composed entirely of cached objects that are pulled together to form a single page view.

Sometimes, we can also be really efficient and make extras when we cook a meal. Perhaps when cooking your omelette, you decide to cook 5 omelettes, which you store for later meals. This is similar to a page cache in that you will build the page cache and store that as an object for later use. Rather than making omelettes 5 times, you can make 5 omelettes at once and store them in the fridge for later meals.

But the comparisons are not done there. An object cache can be implemented with various storage engines just as you can store your food in various storage devices. You can put your haul in the refrigerator, the freezer, a cabinet, the counter, on shelves, in the pantry, in the basement, etc. You make these decisions based on what storage options you have, how full the storage devices are, your access to additional storage devices, etc. When deciding on a storage engine for your application, you mull over these same decisions. Just like it makes good sense to put your eggs in the refrigerator instead of the cupboard, it might make more sense to hold your page cache in Varnish vs. Memcached; however, sometimes you do not have a refrigerator at your disposal, you have to improvise and store your eggs in a cooler full of ice.

### Use the Metaphor

The purpose of this metaphor is to make it clear that you know more about caching than you think you do. You have used caching strategies before. You can get a lot of mileage out of comparing caching in web development with the process of retrieving groceries to prepare meals.

For instance, you would laugh at someone who went to the store and bought a cup of flour every time the individual needed a single cup of flour for a meal. You would instantly realize that this strategy is time consuming, inefficient and expensive. As a web developer, you should have the exact same reaction when a developer pings Twitter’s API to get Tweets every time a page is loaded. This is an expensive process that is slow and inefficient. In both situations, you should recognize the importance of getting the objects that you need and storing them in a place that is easier to access for future use.

Remember that object caching is like grocery shopping. Storing the acquired objects is like putting away the groceries in your house. Building a page cache is making the meal from your cached items. By thinking of your application as an analogy for making a meal, you can gain some insight into inefficiencies in your caching strategy.

## Applying Caching Concepts to WordPress

Now that we have a good understanding of core caching concepts, it is time to apply them to WordPress. In this section of the article, we will focus on the object cache and page cache as there are clear correlates to those concepts in WordPress. There is no fragment cache in WordPress, so that will not be discussed.

### 1. WordPress Object Caching

WordPress implements an object cache through two different mechanisms: `transients` and the `WP_Object_Cache` class. The hallmark of object caching is to provide a persistent caching backend that allows cached data to be available across requests. Both `transients` and the `WP_Object_Cache` class can provide this persistence.

### **2. Transients**

Out of the box, WordPress supports persistent object caching via transients. The WordPress transients API allows you to store, retrieve, and delete objects from the database. By default, the transients cache uses the `wp_options` table for data storage. A point of confusion regarding transients as an object cache often comes from fact that transients are stored in WordPress’s MySQL database table. A MySQL database is a minimally sufficient place to store objects as it can be a location that allows much faster retrieval of data than the object’s original location.

Transients are an excellent object cache option in WordPress because they provide a persistent cache with zero configuration. For plugin and theme developers, you can nearly guarantee that you will be using a persistent cache when you use the transients API. The downside to the transient cache is that it is using MySQL, which is one of the slower and riskier options for storing cached data. It is usually a better strategy to separate the caching engine from the main data store in order to maximize the efficiency of both stores.

### **3. The WP\_Object\_Cache Class**

The [WP\_Object\_Cache](https://developer.wordpress.org/reference/classes/wp_object_cache/) class is a class that defines the storage engine for WordPress’s object cache. This class can be overridden with a custom class, meaning that a developer can configure WordPress to use any storage engine as an object cache. The two most popular in the WordPress world is Memcached and Redis.

The advantage of using the WP\_Object\_Cache class is primarily performance. Using this class allows you to extend WordPress to use the absolute best caching engines in the world. For instance, using Memcached as WordPress’s object cache gives ridiculously fast data access that easily scales to multiple servers. Memcached is an important caching engine for use with high traffic websites. With the WP\_Object\_Cache class, developers can finely tune the caching experience in WordPress, whereas using the transients API gives you very little control over the caching engine. Relating this class back to the metaphor, the WP\_Object\_Cache class allows you to precisely decide where your food will be stored.

As an added benefit of using the WP\_Object\_Cache class, code that uses the transient API will actually use the storage engine in WP\_Object\_Cache class if it is defined. For instance, if you have a finely tuned system using Memcached as the caching engine and you install a plugin that uses the transients API, it will take full advantage of your Memcached installation instead of storing data in the MySQL database.

By default, the WP\_Object\_Cache is defined, but only implements a run-time cache. Since WordPress cannot decide whether or not you have a storage engine available and because it has to make sure that use of the object cache API does not cause fatal errors, it implements a default WP\_Object\_Cache class. This default class merely stores data in a PHP variable during run time and is non-persistent. This, however, can be overridden.

To define your own object cache, you must add a file to `wp-content/` named `object-cache.php`. If this file is defined, it will be loaded instead of the default class. This type of file is known as a WordPress drop-in. A few different `object-cache.php` files exist in the plugin repository for different caching engines like Memcached, Redis or OPcache.

### 4. WordPress Page Caching

Similar to the WP\_Object\_Cache class, WordPress offers a drop-in to define a page cache. By placing a file named `advanced-cache.php` into the `wp-content/` directory, you can define all of the logic related to caching a page.

The general idea for the logic behind the page caching mechanism is as follows:

1. Based on the URL of the request (as well as a few other pieces of information), look in the object cache to see if the cached version of the page exists.
2. If the page exists, serve it and complete the request.
3. If the page does not exist, start output buffering, load the page, finish output buffering and store the output for subsequent requests.

There are some finer nuances to a page caching system, but that logic defines the main mechanism for generating a page cache.

The beauty of `advanced-cache.php` is that it is loaded in the first 1% of the WordPress page load. As such, if the cached page is found, 99% of the WordPress load is avoided, which leads to a significant performance boost in the application. An important thing to note with `advanced-cache.php` is that it needs a persistent cache to store its data. The most effective solution is to use a persistent object cache as the data store, but solid solutions exist that utilize the file system for this cache.

While use of `advanced-cache.php` is the easiest and most accessible form of page caching in WordPress, you can also use a reverse proxy approach with Varnish, Nginx, or a hosted caching solution. We will not go into these solutions here because these are mostly configured at the systems level and have little to do with WordPress specifically. We only touch on this as an alternative to `advanced-cache.php`.

## Caching Tips

Now that you know a little about caching in WordPress, here some nuggets of "wisdom" that has been collected through experience with caching in WordPress. In hope, you can avoid some struggles that some people have faced before.

1. Never depend on your cache for application functionality. You should always develop your application with the assumption that the cache is 100% broken. Whether or not the cache is operational, your application should still provide the intended functionality. Your cache should always be able to regenerate itself if it is corrupted.
2. Use your cache as a "progressive enhancement" for performance. While your application should function without the cache, that does not mean that it should perform well. The caching layer is to provide better performance, not functionality. As such, you can think of the caching layer as a progressive enhancement that improves the performance of the application but still works without caching.
3. Test your application with caching on and off. To verify that your application is functioning properly, it should be tested with and without the cache turned on. Depending on the caching strategy and storage engines used this can be more or less difficult. To avoid really inconvenient surprises, it is best to check the application in both states.
4. Always set an expiration value for every object that is cached. Theoretically, for maximum efficiency, you should only refresh a cached object when it changes; however, you will eventually stumble upon a very difficult to debug situation if you do not set your cached objects to eventually expire. This will help avoid issues of stale data being served.
5. Know the system that you are caching for. Different caching strategies can be used only if certain requirements are met by the environment. It is always best to learn as much about the environment as possible before building the application.

## Conclusion

In this article, we discussed the essential concepts that one must understand in order to be able to apply caching to a web development project. With this information, in hope, you are better prepared to work with caching in your projects. This article is intended to serve as a primer that makes it easier to understand more complex caching concepts.


# OPcache Extension

PHP's OPcache extension review.

## Reminder on OPCodes caches

PHP is a scripting language, that by default will compile any file you ask it to run, obtain OPCodes from a compilation, run them, and trash them away immediately. PHP has been designed like that: it "forgets" everything it's done in request R-1 when it comes to run request R.

{% hint style="success" %}
This article originally from [blog.jpauli.tech](http://blog.jpauli.tech/2015-03-05-opcache-html/)
{% endhint %}

On production servers, the PHP code is very unlikely to change between several requests, thus, the compilation step will always read the same source code, leading to the very exact same OPCode to be run. This is a big waste of time and resources, as the PHP compiler is invoked for every request, for every script.

![](/files/-MGP0-ptnhWTMZCo-d20)

Knowing that compilation can really take a lot of time, OPCode cache extensions have been designed. Their main goal is to compile once and only once each PHP script, and cache the resulting OPCodes into shared memory so that every other PHP worker of your production worker pool (usually using PHP-FPM) can make use of the OPCodes by reading them and executing then back.

The result is a massive boost in the overall performance of the language, dividing time to run a script by a factor of at least 2 (very depend on the script), usually more than 2, as PHP now doesn't have to compile again and again the same PHP scripts.

The boost is higher as the application is more complex. If you take applications running tons of files, like framework based applications, or products like WordPress, you will experience a factor of 10-15 or so. This is because the PHP compiler is slow, and this is just a normal situation: a compiler is slow, whatever it is, because its work is to turn a syntax into another, trying to understand what you asked, and somehow to optimize the generated code for it to later run the fastest as possible; so yes, compiling a PHP script is really slow and eats a lot of memory.

## Introducing OPcache

OPcache has been open-sourced since 2013 and is bundled into PHP's source starting from PHP 5.5.0. It has thus become a standard for PHP OPcode cache solutions. There exist other solutions, such as XCache, APC, Eaccelerator and others.

I will not talk about those other solutions, as I myself don't know them except APC. APC support has been discontinued in favour of OPcache. Short, if you were using APC before, please, use OPcache now.

OPcache has become the real official recommended OPCode cache solution by the developers of PHP. You may still use other solutions if you want, however, never ever activate more than one OPCode cache extension at the same time, you will likely crash PHP.

Be aware that new development involving OPcache won't target PHP 5 branch, but PHP 7 branch which is the nowadays stable branch. This article will target OPcache for PHP 5 and PHP 7, so that you may spot the differences (which are not that big).

So OPcache is an extension, a `zend_extension` more precisely, which is shipped into the PHP source code, starting from PHP 5.5.0 (Pecl for others), and that must be activated through the normal php.ini process of activating an extension. For distros, please refer to your distribution manual to know how PHP and OPcache have been bundled.

## **Two features into one product**

OPcache is an extension which provides two main features:

* OPCodes caching
* OPCodes optimization

Because OPcache triggers the PHP compiler, to get OPCodes and cache them, it could use this step to optimize the OPCodes. Optimizations are basically about compiler optimizations and share many concepts of this computer science discipline. OPcache optimizer is a multi-pass compiler optimizer.

![](/files/-MGP15Gy66KzTbOzeFL3)

## OPcache in deep

Let's now see together how OPcache works internally. If you want to follow the code, you can fetch it from the PHP source code, here it is for PHP 7.0.

Unlike what you can think, OPCode caching is not a that hard concept to analyze and understand. You must have good knowledge on how the Zend Engine works and has been designed, then you should start spotting places where the job can be done.

## Shared memory models

As you know, there exist many shared memory models under different Operating Systems. Under modern Unixes, there exist several ways of sharing memory through processes, most commonly used are:

* System-V shm API
* POSIX API
* mmap API
* Unix socket API

OPcache is able to use the first three of them, as soon as your OS supports the layer. The `INI` setting [`opcache.preferred_memory_model`](https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.preferred-memory-model) allows you to explicitly select the memory model you want.\
If you leave the parameter to a null value, OPcache will select the first model which works for your platform, iterating through its table:

```c
	static const zend_shared_memory_handler_entry handler_table[] = {
	#ifdef USE_MMAP
		{ "mmap", &zend_alloc_mmap_handlers },
	#endif
	#ifdef USE_SHM
		{ "shm", &zend_alloc_shm_handlers },
	#endif
	#ifdef USE_SHM_OPEN
		{ "posix", &zend_alloc_posix_handlers },
	#endif
	#ifdef ZEND_WIN32
		{ "win32", &zend_alloc_win32_handlers },
	#endif
		{ NULL, NULL}
	};
```

So by default, `mmap` should be used. It's a nice memory model, mature and robust. However, it is less informative to the sysadmin that System-V SHM model is, and its `ipcs` and `ipcrm` commands.

As soon as OPcache starts (as soon as PHP starts), OPcache will try a shared memory model and will allocate one big memory segment that it will then divide and manage on its side. However, it will never free this segment back, nor will it try to resize it.

{% hint style="info" %}
OPcache allocates one segment of shared memory when PHP starts, once for all, and never frees it nor fragments it.
{% endhint %}

The size of the memory segment can be told using the [`opcache.memory_consumption`](https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.memory-consumption) INI setting (Megabytes). Size it big, don't hesitate to give space. Never ever run out of shared memory space, if you do, you will lock your processes, we'll get back to that later.

Size the shared memory segment according to your needs, don't forget that a production server dedicated to PHP processes may bundle several dozens of Gigabytes of memory, just for PHP. Having a 1Gb shared memory segment (or more) is not uncommon, it will depend on your needs, but if you use a modern application stack, aka framework based, with lots of dependencies etc.., then use at least 1Gb of shared memory.

The shared memory segment will be used for several things in OPcache:

* Script's data structure caching, involving obviously OPCodes caching but not only.
* Shared interned strings buffer.
* Cached scripts HashTable.
* Global OPcache shared memory state.

So remember, the shared memory segment size will not only contain raw OPCodes but other things needed for OPcache internals. Measure on your side and size it accordingly.

![](/files/-MGP3NrivZi0ZiKnhTc7)

## OPCodes caching

Here we go to detail how the caching mechanism works.

The overall idea is to copy into shared memory (shm) every pointer data that won't change from request to request, aka immutable things. And there are many of them.

After, once loading back the same script: restore every pointer data from shared memory to standard process memory, tied to the current request.

When the PHP compiler is working, it uses Zend Memory Manager (ZMM) to allocate every pointer. This kind of memory used is request bound as ZMM will automatically attempt to free those pointers as soon as the current request finishes. Also, those pointers are allocated from the current process' heap, that is this is some privately mapped memory and thus can't be shared with other PHP processes. Hence, OPcache's job is to browse every structure returned by the PHP compiler, and not leave one single pointer allocated onto this pool, but copy it into a shared memory allocated pool.

And here we talk about compile-time, whatever has been allocated by the compiler, is assumed to be immutable. Non-immutable data will be created at runtime by the Zend Virtual Machine, so it is safe to save everything that the Zend Compiler created, into shared memory.

Examples of such created things: functions and classes, those are functions' name pointers, functions' OPArray pointers, classes' constants, classes declared variable names and eventually their default content. There are really many things that are created in memory by the PHP compiler.

Such a memory model is used to prevent locks at maximum. We'll go back to locks in a later subject, but basically, OPcache does its job all at once, before runtime, so that during the runtime of the script, OPcache has nothing more to do; volatile data will be created on the classical process heap using ZMM, and immutable data would have been restored from shared memory.

So, OPcache hooks into the compiler and replaces the structure this latter should fill-in while compiling PHP scripts, by its own. It then makes the compiler fills a `persistent_script` structure, instead of it filling directly the Zend Engine tables and internal structures.

Here is a `persistent_script` structure:

```c
	typedef struct _zend_persistent_script {
		ulong          hash_value;
		char          *full_path;              /* full real path with resolved symlinks */
		unsigned int   full_path_len;
		zend_op_array  main_op_array;
		HashTable      function_table;
		HashTable      class_table;
		long           compiler_halt_offset;   /* position of __HALT_COMPILER or -1 */
		int            ping_auto_globals_mask; /* which autoglobals are used by the script */
		accel_time_t   timestamp;              /* the script modification time */
		zend_bool      corrupted;
	#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
		zend_uint      early_binding;          /* the linked list of delayed declarations */
	#endif

		void          *mem;                    /* shared memory area used by script structures */
		size_t         size;                   /* size of used shared memory */

		/* All entries that shouldn't be counted in the ADLER32
		 * checksum must be declared in this struct
		 */
		struct zend_persistent_script_dynamic_members {
			time_t       last_used;
			ulong        hits;
			unsigned int memory_consumption;
			unsigned int checksum;
			time_t       revalidate;
		} dynamic_members;
	} zend_persistent_script;
```

And here is how OPcache replaces the compiler structure by the `persistent_script` ones, simple function pointers switch:

```c
	new_persistent_script = create_persistent_script();

	/* Save the original values for the op_array, function table and class table */
	orig_active_op_array = CG(active_op_array);
	orig_function_table = CG(function_table);
	orig_class_table = CG(class_table);
	orig_user_error_handler = EG(user_error_handler);

	/* Override them with ours */
	CG(function_table) = &ZCG(function_table);
	EG(class_table) = CG(class_table) = &new_persistent_script->class_table;
	EG(user_error_handler) = NULL;

	zend_try {
		orig_compiler_options = CG(compiler_options);
		/* Configure the compiler */
		CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY;
		CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES;
		CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING;
		CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION;
		op_array = *op_array_p = accelerator_orig_compile_file(file_handle, type TSRMLS_CC); /* Trigger PHP compiler */
		CG(compiler_options) = orig_compiler_options;
	} zend_catch {
		op_array = NULL;
		do_bailout = 1;
		CG(compiler_options) = orig_compiler_options;
	} zend_end_try();

	/* Restore originals */
	CG(active_op_array) = orig_active_op_array;
	CG(function_table) = orig_function_table;
	EG(class_table) = CG(class_table) = orig_class_table;
	EG(user_error_handler) = orig_user_error_handler;
```

As we can see, the PHP compiler is fully isolated and disconnected from the tables it usually fills; it will now fill the `persistent_script` structures. Then OPcache will have to browse those structures, and replace request allocated pointers to shm ones. OPcache is interested in:

* The script functions.
* The script classes.
* The script main OPArray.
* The script path.
* The script structure itself.

![](/files/-MGP4ujllKO3c30uc3yD)

The compiler is also told some options to disable some optimizations it does, like `ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION` and `ZEND_COMPILE_DELAYED_BINDING`. That would add more work to OPcache. Remember that OPcache hooks into the Zend Engine, it is not a source code patch.

Now that we have a `persitent_script` structure, we must cache its information. Remember that the PHP Compiler has filled-in our structures, but it allocated the memory behind this using the Zend Memory Manager; this memory will be freed at the end of the current request. We then need to browse this memory and copy all of it into the shared memory segment, so that the information we just gathered will now persist through several requests and won't need to be recomputed every time.

The process is as follow:

* Take the PHP script to cache, and compute every variable data size (every pointer target).
* Reserve into already allocated shared memory one big block of this precise size.
* Iterate over the PHP script variable structures, and for each variable-data pointer target, copy it into the just-allocated shared memory block.
* Do the exact opposite for script loading, when this comes to play.

So OPcache is clever about shared memory, and will not fragment it by freeing it and compacting it.\
For every script, it computes the exact size this script needs to store information into shared memory and then copies the data into the segment.

The memory is never freed nor given back to the OS by OPcache thus the memory is perfectly aligned and never fragmented. This gives a big boost in the performance of shared memory, as there is no linked-list or BTree to store and traverse when managing memory that can be freed (like malloc/free do).

OPcache keeps storing things into the shared memory segment, and when the data become stale (because of script revalidation); it does not free the buffers but mark them as "wasted". When the max wasted percentage is reached, OPcache triggers a restart.

This model is very different from the old APC extension, for example, and has the big advantage of providing the same performances as time runs, because the memory buffer from SHM is never managed (freed, compacted, etc...), memory management operations are truly technically stuff which brings nothing to functionalities, but performance penalty as they run.

OPcache has been designed with highest possible performance in mind for the PHP environment runtime, not touching back the shared memory segment provides as well a very good rate of CPU caches hits (especially L1 and L2, as OPcache also aligns the memory pointers for them to better find a hit in an L1/L2 line).

Caching a script thus involves as a first step computing the exact size of its data. Here is the algorithm:

```c
	uint zend_accel_script_persist_calc(zend_persistent_script *new_persistent_script, char *key, unsigned int key_length TSRMLS_DC)
	{
		START_SIZE();

		ADD_SIZE(zend_hash_persist_calc(&new_persistent_script->function_table, (int (*)(void* TSRMLS_DC)) zend_persist_op_array_calc, sizeof(zend_op_array) TSRMLS_CC));
		ADD_SIZE(zend_accel_persist_class_table_calc(&new_persistent_script->class_table TSRMLS_CC));
		ADD_SIZE(zend_persist_op_array_calc(&new_persistent_script->main_op_array TSRMLS_CC));
		ADD_DUP_SIZE(key, key_length + 1);
		ADD_DUP_SIZE(new_persistent_script->full_path, new_persistent_script->full_path_len + 1);
		ADD_DUP_SIZE(new_persistent_script, sizeof(zend_persistent_script));

		RETURN_SIZE();
	}
```

I repeat, what we have to cache are:

* The script functions.
* The script classes.
* The script main OPArray.
* The script path.
* The script structure itself.

For functions, classes and OPArray, the iterating algorithm is deep searching; it caches every pointer data.\
For example for the functions in PHP 5, we must copy into shared memory (shm):

#### **The functions HashTable**

* The functions HashTable buckets table (Bucket \*\*)
* The functions HashTable buckets (Bucket \*)
* The functions HashTable buckets' key (char \*)
* The functions HashTable buckets' data pointer (void \*)
* The functions HashTable buckets' data (\*)

#### **The functions OPArray**

* The OPArray filename (char \*)
* The OPArray literals (names (char \*) and values (zval \*))
* The OPArray OPCodes (zend\_op \*)
* The OPArray function name (char \*)
* The OPArray arg\_infos (zend\_arg\_info \*, and the name and class name as both char \*)
* The OPArray break-continue array (zend\_brk\_cont\_element \*)
* The OPArray static variables (Full deep HashTable and zval \*)
* The OPArray doc comments (char \*)
* The OPArray try-catch array (zend\_try\_catch\_element \*)
* The OPArray compiled variables (zend\_compiled\_variable \*)

I did not detail all, and these changes for PHP 7 as the structures (such as the hashtable) are different.\
The idea is as I expressed it; copy in shared memory every pointer data. As deep copies may involve recursive structures, OPcache uses a translate table for pointer storage; every time it copies a pointer from regular request-bound memory to shared memory, it saves the association between the old pointer address and the new pointer address.

The copy process, before copying, looks up this translate table to know if it has already copied the data if so, it reuses the old pointer data so that it never duplicates any pointer data:

```c
	void *_zend_shared_memdup(void *source, size_t size, zend_bool free_source TSRMLS_DC)
	{
		void **old_p, *retval;

		if (zend_hash_index_find(&xlat_table, (ulong)source, (void **)&old_p) == SUCCESS) {
			/* we already duplicated this pointer */
			return *old_p;
		}
		retval = ZCG(mem);;
		ZCG(mem) = (void*)(((char*)ZCG(mem)) + ZEND_ALIGNED_SIZE(size));
		memcpy(retval, source, size);
		if (free_source) {
			interned_efree((char*)source);
		}
		zend_shared_alloc_register_xlat_entry(source, retval);
		return retval;
	}
```

`ZCG(mem)` represents the fixed-size shared memory segment and is filled-in as elements are added. It then has already been allocated, there is no need to allocate memory on each copy (which would have been less performant), but simply fill-in the memory, and move forward the pointer address border.

We detailed the script caching algorithm, which role is to take any request-bound heap memory pointer and data and duplicate it into shared memory, if not already copied.

The loading algorithm does the exact opposite: it gets the `persistent_script` back from shared memory and browse each of its dynamic structures to duplicate every shared pointer to a request-bound allocated pointer.

The script is then ready to be run by the Zend Engine Executor, as it now doesn't embed any shared pointer address (which would lead to massive bugs of one script modifying the structure of its brother). The Zend Engine is tricked (hooked by OPcache); it has seen nothing of the pointers replacement happening before the execution happens.

This process of copying from regular memory to shared memory (cache script), or the opposite (load script), is highly optimized, and even if it involves many memory copies or hash lookups, which are not really nice in term of performance, we are way faster than triggering the PHP compiler every time.

## Sharing interned strings

Interned strings are a nice memory optimisation that's been added to PHP 5.4. This may feel like some commonsense; every time PHP meets an immutable string (a char\*), it stores it into a special buffer and reuses the pointer for every occurrence of this same string next to come.

You may learn more about interned strings [from this article](http://blog.jpauli.tech/2015/09/18/php-string-management.html#interned-strings). Interned strings are about immutable strings, and thus are nearly exclusively used into the PHP compiler.

Interned strings work like this:

![](/files/-MGP71I6d6R_2IsqYOLI)

The same instance of a string is shared to every pointer. But there still is a problem with that; this interned string buffer is a per-process buffer, it is managed by the PHP compiler mainly. That means that in a PHP-FPM pool, every PHP worker will store its own copy of this buffer, something like this:

![](/files/-MGP7FKYrLC9MFDoDdUE)

This leads to a massive waste of memory, especially in case you have tons of workers (you're likely to have), and you use very big strings in your PHP code (tip: PHP's annotation comments are strings). What OPcache takes care of, is sharing this buffer between every PHP worker of a pool. Something like this:

![](/files/-MGP7Oh-SZu54A1bik96)

Et voila! OPcache shares the interned string buffers of all the PHP-FPM worker of the same pools and uses its shm segment to store those.

Thus, you need to size the shm segment according to your interned strings usage as well. Also, OPcache allows you to tune the interned strings shm usage using `opcache.interned_strings_buffer` INI setting. Monitor OPcache and once more; make sure you have enough memory.

However here, if you run out of interned strings memory space (`opcache.interned_strings_buffer`setting is too low), OPcache will not trigger a restart, because it still has some shm available, only interned strings buffer is full, which is not blocking to continue processing request, you'll simply end up having some strings interned and shared, and some other that use PHP worker's memory. I don't recommend that for performance.

Read your logs, when you run out of interned string memory, OPcache warns you:

```c
	if (ZCSG(interned_strings_top) + ZEND_MM_ALIGNED_SIZE(sizeof(Bucket) + nKeyLength) >=
			ZCSG(interned_strings_end)) {
			/* no memory, return the same non-interned string */
			zend_accel_error(ACCEL_LOG_WARNING, "Interned string buffer overflow");
			return arKey;
		}
```

{% hint style="info" %}
Interned strings are about every piece of immutable string the PHP compiler is going to meet while doing its job; variable names, "php strings", function names, class names... PHP comments, nowadays used and called "annotations", are strings as well, and they are usually huge strings, that will eat most of your interned strings buffer. Think about them as well.
{% endhint %}

## The locking mechanism

As soon as we talk about shared memory (shm), we must talk about memory locking mechanisms.\
The baseline is simple; every PHP process that is willing to write into shared memory will lock every other process willing to write into shared memory as well. So the critical section is done on write operations, and not read operations.

You may happen to have 150 PHP processes reading the shared memory, only one of them may write into the shm at the same time, write operation doesn't prevent read operation but another write operation.

So, there should be no dead-lock in OPcache, until you don't prime your cache smoothly. If, after your code deployment, you open your webserver to traffic, then there will be a massive rush on your scripts to compile and cache them, and as the cache write-to-shm operation is done under exclusive lock, you will probably lock every process once the first lucky one has obtained a lock to write.

When this latter will release the lock, every process waiting for it will then see that the file they just compiled is already stored into shm and then they will trash the compilation result to load it from shm. This is a big waste of resources.

```c
	/* exclusive lock */
	zend_shared_alloc_lock(TSRMLS_C);
	
	/* Check if we still need to put the file into the cache (may be it was
	 * already stored by another process. This final check is done under
	 * exclusive lock) */
	bucket = zend_accel_hash_find_entry(&ZCSG(hash), new_persistent_script->full_path, new_persistent_script->full_path_len + 1);
	if (bucket) {
		zend_persistent_script *existing_persistent_script = (zend_persistent_script *)bucket->data;

		if (!existing_persistent_script->corrupted) {
			if (!ZCG(accel_directives).revalidate_path &&
			    (!ZCG(accel_directives).validate_timestamps ||
			     (new_persistent_script->timestamp == existing_persistent_script->timestamp))) {
				zend_accel_add_key(key, key_length, bucket TSRMLS_CC);
			}
			zend_shared_alloc_unlock(TSRMLS_C);
			return new_persistent_script;
		}
	}
```

What you should do, is cut off your server from external web traffic, deploy your new code, curl some of your most heavy URLs, so that your curl requests will smoothly prime the shm. When you think you are done with the big majority of your scripts, you may now open your webserver to traffic, so that now this one will massively read shm, which is a lock-free operation.

Sure there may still be some little scripts not compiled yet, but as soon as they are uncommon, there is no pressure on the write lock.

What you should avoid, is writing PHP files at runtime, and then make use of them. For the exact same reason; as soon as you write a new PHP file onto your production server documentroot, and you make use of it, chances are that it will be rushed by thousands of PHP workers trying to compile and cache it into shm; you will lock.

Those dynamically generated PHP files should be added to the OPcache blacklist, using the `opcache.blacklist-filename` INI setting (which accepts glob patterns).

Technically speaking, the lock mechanism is not very strong, but it works on many flavours of Unix; it uses the famous `fcntl()` call.

```c
	void zend_shared_alloc_lock(TSRMLS_D)
	{
		while (1) {
			if (fcntl(lock_file, F_SETLKW, &mem_write_lock) == -1) {
				if (errno == EINTR) {
					continue;
				}
				zend_accel_error(ACCEL_LOG_ERROR, "Cannot create lock - %s (%d)", strerror(errno), errno);
			}
			break;
		}
		ZCG(locked) = 1;
		zend_hash_init(&xlat_table, 100, NULL, NULL, 1);
	}
```

I here talked about memory locks happening on the normal process; nothing bad, if you take care, no more than one PHP process should be writing to the shm at the same time, so you won't suffer from any lock waiting times.

There exists however another lock that you should prevent from happening; the memory exhausted lock. This is the next chapter.

## Understanding the OPcache memory consumption

So I remind you with facts:

1. OPcache creates one unique segment of shared memory, once for all, at PHP startup (when you start PHP-FPM).
2. OPcache never frees some shm into this segment, the segment is allocated at startup, then filled in according to the needs.
3. OPcache locks shm when it writes into it.
4. shm is used for several purposes:
   * Script's data-structure caching, involving obviously OPCodes caching but not only.
   * Shared interned strings buffer.
   * Cached scripts HashTable.
   * Global OPcache shared memory state.

If you use validation of your scripts, OPcache will check their modification date at every access (not every, check `opcache.revalidate_freq` INI setting), and will have a hint of whether the file is fresh or stale.

This check is cached; it is not costly as opposed to what you could think. OPcache comes into the scene sometime after PHP, and PHP has already `stat()`ed the file; OPcache just reuses this information and does not issue a costly `stat()` call to the filesystem again for its own use.

If you use timestamp validation, via `opcache.validate_timestamps` and `opcache.revalidate_freq`, and your file has effectively changed, then OPcache will simply invalidate it, and flag all of its shm data as invalid.

It will not free anything from shm. OPcache flags the shm parts as "wasted". Only when OPCache runs out of shm on an allocation AND when wasted memory reaches the `opcache.max_wasted_percentage` INI setting value, OPcache will trigger a full restart, which is something you must absolutely prevent from happening No other scenario.

```c
	/* Calculate the required memory size */
	memory_used = zend_accel_script_persist_calc(new_persistent_script, key, key_length TSRMLS_CC);

	/* Allocate shared memory */
	ZCG(mem) = zend_shared_alloc(memory_used);
	if (!ZCG(mem)) {
		zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM TSRMLS_CC);
		zend_shared_alloc_unlock(TSRMLS_C);
		return new_persistent_script;
	}
```

![](/files/-MGP8OMpI-Bzd_sJBPy4)

The picture above details what your shm segment could look like after some time has passed and some scripts have changed. The changed scripts' memory has been marked as "wasted", and OPcache will simply now ignore those memory areas, as well as it will recompile your changed scripts and create a new memory segment for their information's.

When enough wasted memory is reached, a restart will happen, OPcache will then lock shm, reset the shm segment (empty it entirely), and release the lock. This will let your server in a situation like if it has just started; every PHP worker is going to stress the lock now because every worker will try to compile some files; your web server will now suffer from very poor performance because of locks.

The more the load, the less performance, this is unfortunately the rule with locks. So your server may really suffer for long seconds now.

{% hint style="info" %}
Never run out of shared memory
{% endhint %}

More generally, what you should do is disable script modification tracking on a production server, that way you are sure the cache will never trigger a restart (this is not entirely true as OPcache may still run out of persistent script keyspace, we'll see that later). A classic deployment should follow the rules:

* Take out the server from the load (disconnect it from your load balancer).
* Empty OPcache (call `opcache_reset()`) or directly shut down FPM (better, we'll detail in few minutes).
* Deploy a new version of your application at once.
* Restart your FPM pool if needed and prime your new cache smoothly by triggering curl request on major application entry points.
* Open back your server to traffic.

All this can be done with a 50 line shell script that can be turned very robust playing with `lsof` and `kill` in case some hard requests don't seem to finish. Bring your Unix knowledge ;-).

You can even see what happens using one of the numerous GUI frontends for OPcache available anywhere on the web and Github, they all make use of the `opcache_get_status()` function:

![](/files/-MGP9jo3OfIQCae9S4yj)

This is not the full story though, there is another thing to clearly keep in mind; **cache keys**.

When OPcache stores a cached script into SHM, it stores it's into a HashTable, to be able to find the script back after. But it has to choose a key to index the HashTable. What index/key does OPcache use to achieve this goal? This highly depends on both the configuration and the way your app has been designed.

Normally, OPcache resolves the full path to the script, but take care as it uses the PHP's realpath cache and you may suffer from it. If you change your documentroot using a symlink, put `opcache.revalidate_path` to 1 and empty your realpath cache (which may be hard to do as it is bound to the PHP worker process handling the current request).

So, OPcache resolves the path to the file, and when resolved, it uses the realpath string as a cache key for the script, and that's all, assuming you have `opcache.revalidate_path` INI setting turned to 1. If not, OPcache will also use the **unresolved path** as a cache key, and that will lead to problems if you were using symlinks, because if you then change the symlink target, OPcache will not notice it, as it will still use the unresolved path as key to find the old targetted script (this is to save a symlink resolution call).

By turning `opcache.use_cwd` to 1, you tell OPcache to prepend the `cwd` to every key, in case you use relative paths to include your files, like `require_once "./foo.php";`. I suggest, if you use relative paths and host several applications on the same PHP instance (which you shouldn't do), to always put `opcache.use_cwd` to 1. Also, if you happen to play with symlinks, turn *opcache.revalidate\_path* to 1. But even with those settings on, you will suffer from PHP's realpath cache, and you may change the \_www \_symlink to another target, it won't be noticed by OPcache, even if you empty the cache by using `opcache_reset()`.

{% hint style="info" %}
Because of PHP's realpath cache, you may experience problems if using symlinks to handle your documentroot for deployment. Turn *opcache.use\_cwd* and *opcache.revalidate\_path* to 1, but even with those settings, bad symlink resolutions may happen, this is because PHP answers OPcache realpath resolution requests with a wrong answer, coming from its realpath\_cache mechanism.
{% endhint %}

If you want to be extra safe in your deployment, the first option is to not use symlinks to manage your documentroot.

If not, then use a double FPM pool, and use a FastCGI load balancer to balance between the two pools when deploying. Lighttpd and Nginx have this feature enabled by default as far as I remember:

* Take out the server from the load (disconnect it from your load balancer).
* Shut down FPM, you will kill PHP (and then OPcache) and will be extra safe especially about PHP's realpath cache, which may trick you. This latter will be cleared if you shut down FPM. Monitor the eventual workers that may be stuck, and kill them if necessary.
* Deploy a new version of your application at once.
* Restart your FPM pool. Don't forget to prime your new cache smoothly by triggering curl requests on major application entry points before.
* Open back your server to traffic.

If you don't want to take your server out of the balancer, what can be done then, is:

* Deploy your new code into another directory, as your PHP server has one FPM pool still active and serving production requests.
* Start another FPM pool, listening on another port, while still having the first FPM pool active and serving production requests.
* Now you have two FPM pools, one hot and working, one idle, waiting to be bound to requests.
* Change your documentroot symlink target to target the new deploy path, and immediately after, stop the first FPM pool. If you told your webserver about your two pools, it should notice the first pool is dying, and should load balance traffic to the new pool now, with no traffic interruption nor failing requests. The second pool will then be triggered, will resolve the new docroot symlink (as it is fresh and has a cleared realpath cache), and serve your new content. This clearly works, I used that on production servers many times, a \~80 lines well-written shell script can take care of all this job.

So depending on the settings, one unique script may lead to several keys computed by OPcache. But the key store is not infinite; it is also allocated into shared memory and may get full, in which case even if there is still a lot of room into the shm, because the persistent script hashtable is full, OPcache will behave like if it had no more memory, and will trigger a restart for next requests.

{% hint style="info" %}
You always should monitor the number of keys in the key store, for it never to be full.
{% endhint %}

OPcache gives you this information with the use of `opcache_get_status()`, a function the different GUIs rely on. The `num_cached_keys` dimension returned by this function gives the info. You should preconfigure the number of keys, as a hint, using `opcache.max_accelerated_files` INI setting.

Take care as the name suggests a number of files, in fact, it is the number of keys that OPcache will compute, and as we've seen, one file may lead to several keys being computed. Monitor it, and use the right number. Avoid using relative paths in `require_once` statements, it makes OPcache generate more keys. Using an autoloader is recommended, as this one, if well configured, will always issue `include_once` calls with full paths, and not relative ones.

{% hint style="info" %}
OPcache preallocates the HashTable to store future persistent scripts when it starts (when PHP starts), and never tries to resize it. If it gets full, it will then trigger a restart. This is done for performance reasons.
{% endhint %}

So this is why you may see a `num_cached_scripts` a dimension which is different from the `num_cached_keys` dimension, from OPcache status report. Only the `num_cached_keys` info is relevant if it reaches `max_cached_keys,` you'll be in trouble with a restart pending.

Do not forget that you can understand what happens by lowering OPcache's log level (`opcache.log_verbosity_level` INI). It tells you if it runs out of memory, and which kind of OOM (OutOfMemory) error it generated; if it is related to the shm being full, or if it is the keys Hashtable which is full.

![](/files/-MGPAP0fY32TOTMd0tc5)

```c
	static void zend_accel_add_key(char *key, unsigned int key_length, zend_accel_hash_entry *bucket TSRMLS_DC)
	{
		if (!zend_accel_hash_find(&ZCSG(hash), key, key_length + 1)) {
			if (zend_accel_hash_is_full(&ZCSG(hash))) {
				zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
				ZSMMG(memory_exhausted) = 1;
				zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH TSRMLS_CC);
			} else {
				char *new_key = zend_shared_alloc(key_length + 1);
				if (new_key) {
					memcpy(new_key, key, key_length + 1);
					if (zend_accel_hash_update(&ZCSG(hash), new_key, key_length + 1, 1, bucket)) {
						zend_accel_error(ACCEL_LOG_INFO, "Added key '%s'", new_key);
					}
				} else {
					zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM TSRMLS_CC);
				}
			}
		}
	}
```

So, to conclude about memory usage, here is the picture:

![](/files/-MGPAd8HPIT0Lj8-pO6g)

When you start PHP, you start OPcache, it allocates immediately `opcache.memory_consumption` Megabytes of shared memory (shm) from the OS.

It then starts using this space, and stores into it the interned strings buffer (`opcache.interned_strings_buffer`). After that, it preallocates the HashTable for future persistent scripts and their keys to be stored. The space used depends on the `opcache.max_accelerated_files`.

Now, a part of the shm is used by OPcache internals, and the non-occupied space left is dedicated to you; to your scripts data structures. This (actually free) memory segment will then be filled in, and as your scripts will change and OPcache will recompile them (assuming you told it to), the space will slowly become "wasted"; except if you tell OPcache not to recompile changed scripts (recommended).

That may look like something like this:

![](/files/-MGPAo-l6V3jQ2VTJmGE)

If persistent scripts HashTable becomes full, or if free SHM runs out, OPcache will trigger a restart (which you'd want to prevent absolutely).

## Configuring OPcache

If you use a framework based application, like a Symfony based application, I strongly suggest:

* Turn off revalidation mechanism on production (turn `opcache.validate_timestamps` to 0).
* Deploy using a full new runtime of your scripts, this is the case with Symfony applications.
* Size correctly your buffers:
  1. `opcache.memory_consumption`, the most important.
  2. `opcache.interned_strings_buffer` , monitor your usage, and size accordingly, take care if you tell OPcache to save comments, which you will likely do if you use PHP "annotations" .(`opcache.save_comments`\_ = 1\_), those are strings, big strings, that will eat your interned strings buffer
  3. `opcache.max_accelerated_files` , numbers of keys to preallocate, once more: monitor and size accordingly.
* Turn off `opcache.opcache.revalidate_path` and `opcache.use_cwd`. That will save some keyspace.
* Turn on `opcache.enable_file_override` , this will accelerate the autoloader.
* Fill-in `opcache.blacklist_filename` list with the script names you are likely to generate during runtime; shouldn't be too many of them anyway.
* Turn off `opcache.consistency_checks`, this basically checks a control sum on your scripts, that eats perf.

With those settings, your memory should never get wasted, then `opcache.max_wasted_percentage` is not very useful in this case.

With those settings, you'll need to turn off your main FPM instance when deploying. You may play with several FPM pools to prevent service downtime like explained earlier. That should be enough.


# OPcache Optimisations

OPcache compiler's optimizer.

## **Introducing**

So we talked about caching OPCodes into shm and loading them back later. Just before caching them, OPcache may also run optimizer passes.

{% hint style="success" %}
This article originally from [blog.jpauli.tech](http://blog.jpauli.tech/2015-03-05-opcache-html/)
{% endhint %}

To fully understand the optimizer, you have to have a good knowledge of how the Zend VM Executor works. Also, you may bring your compiler knowledge, if you are very new to such concepts, perhaps starting [reading some articles on the subject](https://msdn.microsoft.com/en-us/magazine/dn904673.aspx) may help?. Or at least the mandatory-reading [Dragon Book](http://en.wikipedia.org/wiki/Compilers:_Principles,_Techniques,_and_Tools)? Anyway, I'll try to make the subject understandable and fun to read.

Basically, the optimizer is given the whole OPArray structure, and may now browse it, find flaws, and fix them. But as we are analyzing OPCodes **at compile-time**, we have no clue at all on everything tied to a "PHP variable". Basically, we don't know yet what will be stored in any `IS_VAR` or `IS_CV` operand, but only in `IS_CONST` or sometimes in `IS_TMP_VAR`.

Like in any compiler for every language; we must create the most optimized structure to be run at runtime so that the runtime will be the fastest as possible.

OPcache optimizer can optimize a lot of things in `IS_CONST`; We can also replace some OPCodes by others (more optimized at runtime), we also find and trash dead code branches by using a CFG (control flow graph) analysis, but we don't unroll loops, or process to loop invariant motions as such optimizations are hard to apply to PHP.

We also have other possibilities related to PHP internals; we may change the way classes are bound to optimize a bit the process in some specific cases, but we have absolutely not the possibility to do some cross file optimizations, because OPcache plays with OPArrays coming from file compilation (among other functions' OPArrays), and there is total isolation of those OPArrays.

PHP has never been built on a cross file-based VM; the Virtual Machine and the language is file bound; when compiling a file, we have absolutely no information about the files that already got compiled, and those to come next.

We then must try to optimize on a file-by-file basis, and must not assume for example that class A will be present in the future if it is not at the moment. This is very different from Java or C++ that compile using compilation units and allowing cross-file optimizations; PHP simply won't do that, it's not been designed like that.

{% hint style="info" %}
The PHP compiler acts on a file basis and has no shared state through file compilations, it doesn't compile a project in its whole, but a file, followed by others. There is no room for cross file optimizations.
{% endhint %}

OPcache optimization passes can be enabled on a case-by-case basis, using the INI setting `opcache.optimization_level`. It should represent a mask for optimizations you'd like to see enabled, based on their binary values:

```c
	/* zend_optimizer.h */
	#define ZEND_OPTIMIZER_PASS_1		(1<<0)   /* CSE, STRING construction     */
	#define ZEND_OPTIMIZER_PASS_2		(1<<1)   /* Constant conversion and jumps */
	#define ZEND_OPTIMIZER_PASS_3		(1<<2)   /* ++, +=, series of jumps      */
	#define ZEND_OPTIMIZER_PASS_4		(1<<3)   /* INIT_FCALL_BY_NAME -> DO_FCALL */
	#define ZEND_OPTIMIZER_PASS_5		(1<<4)   /* CFG based optimization       */
	#define ZEND_OPTIMIZER_PASS_6		(1<<5)
	#define ZEND_OPTIMIZER_PASS_7		(1<<6)
	#define ZEND_OPTIMIZER_PASS_8		(1<<7)   
	#define ZEND_OPTIMIZER_PASS_9		(1<<8)   /* TMP VAR usage                */
	#define ZEND_OPTIMIZER_PASS_10		(1<<9)   /* NOP removal                 */
	#define ZEND_OPTIMIZER_PASS_11		(1<<10)  /* Merge equal constants       */
	#define ZEND_OPTIMIZER_PASS_12		(1<<11)  /* Adjust used stack           */
	#define ZEND_OPTIMIZER_PASS_13		(1<<12)
	#define ZEND_OPTIMIZER_PASS_14		(1<<13)
	#define ZEND_OPTIMIZER_PASS_15		(1<<14)  /* Collect constants */

	#define ZEND_OPTIMIZER_ALL_PASSES	0xFFFFFFFF

	#define DEFAULT_OPTIMIZATION_LEVEL  "0xFFFFBFFF"
```

## **Known constant statements and branch trashing**

Note that many compile-time known constant statements are NOT computed by the compiler but by OPCache, for PHP 5. In PHP 7, those are computed in the compiler.

Here we go with examples:

```php
	if (false) {
		echo "foo";
	} else {
	   echo "bar";
	}
```

This leads in classical compilation to:

![](/files/-MGPS-pYuTuYNPE5FFeQ)

And optimized compilation:

![](/files/-MGPSAIkXvXDJqCsUtQO)

As we can see, the dead code in the `if(false)` branch has been trashed, the Zend VM executor will then simply have to run a `ZEND_ECHO` OPcode. We then saved some memory, because we threw away some OPCodes, and we may save a little bit of CPU cycles at runtime as well.

I recall you that we cannot know the content of any variable yet, as we are still at compile time (we are between compilation and execution). A code with an `IS_CV` operand instead of `IS_CONST`, could not have been optimized:

```php
	/* That cant be optimized, what's in $a ? */
	if ($a) {
		echo "foo";
	} else {
	   echo "bar";
	}
```

Let's take another example so that you see the differences between PHP 5 and PHP 7:

```php
	if (__DIR__ == '/tmp') {
		echo "foo";
	} else {
	   echo "bar";
	}
```

In PHP 7, the constant `__DIR__` will be substituted and the equality check will be performed by the PHP 7 compiler, that is without OPcache. However, the branch analysis and the branch dead code removing is still done by an OPcache optimizer pass.

In PHP 5 however, the constant `__DIR__` is still substituted, but the equality check is not performed by PHP 5 compiler. This latter is performed by OPcache.

So here to sum up things, if you run both PHP 5 and PHP 7 with OPcache optimizer activated, you will end up to the exact same optimized OPCodes. But if you don't run OPcache optimizer, then the PHP 5 compiled code will be less efficient than the equivalent PHP 7 one, because the PHP 5 compiler doesn't perform any evaluation, whereas PHP 7 compiler computes a lot of things by itself (without the need of OPcache optimizer that would come later).

## **Constant functions pre-evaluation**

However, OP**c**ache is able to turn some `IS_TMP_VAR` to `IS_CONST`. That is, OPcache can compute itself at compile-time, some known values.

Some functions can be run at compile-time because their result will be constant. This is the case of several of them:

\* `function_exists()` and `is_callable()`, for internal functions only.\
\* `extension_loaded()`, if `dl()` is disabled in userland.\
\* `defined()` and `constant()` for internal constants only.\
\* `dirname()` if the argument is constant.\
\* `strlen()` and `dirname()` with constant argument (PHP 7 only).

So look at that example:

```php
	if (function_exists('array_merge')) {
		echo 'yes';
	}
```

Here, if the optimizer is disabled, the compiler generates many work to do for the runtime:

![](/files/-MGPTTfTPNnC1N85k1mt)

Optimized as:

![](/files/-MGPTlI07l2jpmzLEtnS)

Notice that those functions don't compute userland-based. For example:

```php
if ( function_exists('my_custom_function') ) { }
```

Is not optimized, because you are very likely to have (or not) defined the `my_custom_function` is another file. And remember, the PHP compiler and OPcache optimizer only works on a file basis. Even if you do this:

```php
function my_custom_function() { }
if ( function_exists('my_custom_function') ) { }
```

That will not be optimized, because this is too unlikely to happen, the function call optimizer only works for internal types (internal functions, internal constants).

Another example with `dirname()` (PHP 7 only):

```php
	if (dirname(__FILE__) == '/tmp') {
		echo 'yo';
	}
```

Not optimized:

![](/files/-MGPUf8F2DiRFKrhwnqh)

Optimized:

![](/files/-MGPTlI07l2jpmzLEtnS)

Again, `strlen()` is optimized in PHP 7. If we chain them together, we obviously meet a nice optimization. Like this:

```php
	if (strlen(dirname(__FILE__)) == 4) {
		echo "yes";
	} else {
		echo "no";
	}
```

Not optimized:

![](/files/-MGPVZP7s7w3LQgaw-ZQ)

Optimized:

![](/files/-MGPViB8qfcfREWubSCX)

For the example above, you can notice that every statement has been computed at compile/optimization time, and then OPcache optimizer trashed all the 'false' branch (assuming obviously that the 'true' part was chosen).

## **Transtyping**

OPCache optimizer may switch your `IS_CONST` operand types, when it knows runtime will have to transtype them. That effectively saves some CPU cycles at runtime:

```php
	$a = 8;
	$c = $a + "42";
	echo $c;
```

Classical compilation:

![](/files/-MGPW8ZXnTsYoNsXwvpF)

Optimized compilation:

![](/files/-MGPWIMp9Zly8q9uz8Bk)

Look at the second operand true type of `ZEND_ADD` operation; it has switched from a string to an `int`. The optimizer did the job of transtyping the argument type for the math add operation. If it had not; the runtime VM would have done it again, and again, and again as the code is run again, and again, and again. This saves some CPU cycles involved in the transtyping operation.

Here is the OPcache optimizer code that does such a job:

```c
	if (ZEND_OPTIMIZER_PASS_2 & OPTIMIZATION_LEVEL) {
		zend_op *opline;
		zend_op *end = op_array->opcodes + op_array->last;

		opline = op_array->opcodes;
		while (opline < end) {
			switch (opline->opcode) {
				case ZEND_ADD:
				case ZEND_SUB:
				case ZEND_MUL:
				case ZEND_DIV:
					if (ZEND_OP1_TYPE(opline) == IS_CONST) {
						if (ZEND_OP1_LITERAL(opline).type == IS_STRING) {
							convert_scalar_to_number(&ZEND_OP1_LITERAL(opline) TSRMLS_CC);
						}
					}
					/* break missing *intentionally* - the assign_op's may only optimize op2 */
				case ZEND_ASSIGN_ADD:
				case ZEND_ASSIGN_SUB:
				case ZEND_ASSIGN_MUL:
				case ZEND_ASSIGN_DIV:
					if (opline->extended_value != 0) {
						/* object tristate op - don't attempt to optimize it! */
						break;
					}
					if (ZEND_OP2_TYPE(opline) == IS_CONST) {
						if (ZEND_OP2_LITERAL(opline).type == IS_STRING) {
							convert_scalar_to_number(&ZEND_OP2_LITERAL(opline) TSRMLS_CC);
						}
					}
					break;
		/* ... ... */
```

You should note however, that such optimization has been merged into the PHP 7 compiler. That means that even with OPcache disabled (or optimizations disabled), PHP 7 compiler already performs such an optimization, as well as many more that were not performed by the PHP 5 compiler.

A little bit more silly, but adding two `IS_CONST` expressions, the result can then be computed at compile-time, something the PHP compiler does not do by default in PHP 5, OPcache optimizer is needed:

```php
	$a = 4 + "33";
	echo $a;
```

Classical compilation:

![](/files/-MGPWuYbZ5vhU4bkrjic)

Optimized compilation:

![](/files/-MGPX2x9ek3d_OUu5x7S)

The optimizer computed the maths for `4 + 33`, and erased the `ZEND_ADD` operation to be run by replacing it directly by the result. This saves again some CPU at runtime, as the VM executor now has less job to do. Here again, this is done in PHP 7 by the compiler, whereas in PHP 5 you would need OPcache optimizer to do that.

## **Optimized OPCodes substitution**

Now let's dive deeper into OPCodes. Sometimes (rarely), it is possible to substitute a following of OPCodes by other ones, more optimized. Look at that:

```php
	$i = "foo";
	$i = $i + 42;
	echo $i;
```

Classical compilation:

![](/files/-MGPXXQw11pzOWXwjZ8X)

Optimized compilation:

![](/files/-MGPXaVMmo5kQ9kW2Qjw)

Here, our knowledge of the Zend VM executor leads us to substitute a `ZEND_ADD` plus a `ZEND_ASSIGN`, into a `ZEND_ASSIGN_ADD`, usually involved in statements such as `$i+=3;`\
`ZEND_ASSIGN_ADD` is more optimized, it is one OPCode instead of two (which usually is better, but not every time)**.**

On the same subject:

```php
	$j = 4;
	$j++;
	echo $j;
```

Classical compilation:

![](/files/-MGPY-95mPzG4dCy7OT_)

Optimized compilation:

![](/files/-MGPY3aoFdHGmrVm91Rc)

Here, OPcache optimizer replaced the `$i++` by a `++$i` statement, because it had the same meaning in this piece of code. `ZEND_POST_INC` is not very nice OPCode, because it must read the value, return it as-is, but increment a temporary value in memory, whereas `ZEND_PRE_INC` plays with the value itself, and reads it, increments it and returns it (this is just the PRE vs POST incrementation difference).

Because the intermediate value returned by `ZEND_POST_INC` is not used in the script above, the compiler must issue a `ZEND_FREE` OPCode, to free it from memory. OPcache optimizer turns the structure into a `ZEND_PRE_INC`, and removes the useless `ZEND_FREE` ; less job to figure out at runtime.

## **Constant substitution and precomputing**

What about PHP constants? They are more complex than what you think (much more in fact). So some optimizations that may seem obvious actually don't happen for many reasons, but let's see the actual ones:

```php
	const FOO = "bar";
	echo FOO;
```

Classical compilation:

![](/files/-MGPYfy7ngENopncrbpo)

Optimized compilation:

![](/files/-MGPYm2yfO9NggPuXrqS)

This is part of temporary variables optimizations, as we can see, here, once again, one OPCode has been trashed, the result of constant reading is directly figured out at compile time, into the optimizer, and the runtime will have less work to do.

Also, that ugly `define()` function can be replaced by a `const` statement, if its argument is constant:

```php
	define('FOO', 'bar');
	echo FOO;
```

The non-optimized OPCodes from this little script are horrible in term of performance:

![](/files/-MGPZFECLb-J4K1DIg4x)

Optimized, is as expected:

![](/files/-MGPYm2yfO9NggPuXrqS)

`define()` is ugly, because it declares a constant but runs such a job at runtime, issuing a function call (`define()` is really a function). This is very bad.

The `const` keyword leads to a `DECLARE_CONST` OPCode. Note that in PHP 7, `define()` may lead to a const construct into the compiler directly (no optimizer needed).

## **Multiple jump target resolution**

This is actually a little bit hard to detail, but as usual with a simple example, you'll understand.\*\* \*\*This optimization is about jump targets in jump opcodes (there are several flavours of them).

Every time the VM must jump, a jump address is computed by the compiler and stored into the VM operand. A jump is the result of a decision when the VM meets a decision point.

There are lots of jumps into PHP scripts. `if`, `switch`, `while`, `try`, `foreach`, `?:` ... are PHP statements making a decision, if the decision is true: jump to branch A, if not, jump to branch B.

Such algorithms can be optimized if the jump target is itself a jump. The landing jump will then make the VM jump again, to a final landing jump. Multiple jump target resolution is about directly making the VM jump to the final target.

Something like that:

```php
	if ($a) {
		goto a;
	} else {
		echo "no";
	}

	a:
	echo "a";
```

With classical compilation, we end up with such OPCodes:

![](/files/-MGP_3va9VeG6EKnzFb7)

Translated (just read it) as: "if the result of $a evaluation is zero, jump to target 3, in target 3 echo "no". If not, continue, and meet a jump to 4. In 4, echo "a".

This is something like "Jump to 3, and in 3, jump to 4". Why not "jump to 4" directly then?. This is what the optimization does:

![](/files/-MGP_OsAqZGDbF7D63X6)

Here, we can translate that by "if $a evaluation is not zero, jump to 2 which echoes "a", if not, echo "no", much simpler isn't it?.

This optimization shows true power in case of very complex scripts with many levels of decisions. Like having a `while` into an `if`, in which a `goto` is performed, leading to a `switch` which performs `try-catches` , etc...

Without this optimization, the overall OPArray may contain tons of OPCodes. Those will mainly be jumps, but probably jumps leading to jumps. Activating this optimization can sometimes (depend on the script) reduce significantly the number of OPCodes and ease the path the VM will branch; leading in little gain of performances at runtime.

## **Concluding**

I did not show you all the work done by the optimizer. It can also optimize embedded loops by issuing "early returns" for example. Same for embed try-catch blocks or switch-breaks. PHP function calls, which is a heavy process into the engine, is also optimized when possible.

{% hint style="info" %}
The main difficulty in optimizer passes, is to never change the meaning of the script, and especially its control flow.
{% endhint %}

The main difficulty in optimizer passes, is to never change the meaning of the script, and especially its control flow. Bugs were found about this some time ago in OPcache, and it is all but cool when you come to see that PHP executor doesn't behave the way it should, having your little PHP script written under your eyes. In fact, the OPCodes generated have been altered by the optimizer and the engine just runs something which is wrong. Not cool.

Nowadays, OPcache optimizer is pretty stable but still under development for next PHP versions. It had to be patched in deep for PHP 7 as that latter changed many things in internal structures design, as well as having a PHP 7 compiler doing much more optimization job (the most trivial however) than PHP 5 used to do (PHP 5 compiler really does not optimize anything).

{% hint style="info" %}
The PHP 7 compiler is much more efficient than PHP 5's. A lot of optimizations before performed in PHP 5 OPcache are now embedded directly into PHP 7's heart.
{% endhint %}

#### End

We've seen that OPcache has finally become the standard recommended PHP OPCode caching solution. We detailed how it works, not that hard to understand, but error-prone yet.

Nowadays, OPcache is very mature/stable and achieves its goal of boosting dramatically the overall performance of the PHP language by both cancelling the time needed to compile a script and by optimizing the OPCodes resulting of the compilation.

Shared memory is used for every process of a PHP pool to be able to access structures that have been added by others. Interned strings buffer is also managed in shared memory, leading to even more memory savings in a PHP pool of workers - typically using PHP-FPM SAPI.


# Web Hosting I/O Usage

All You Need To Know About Web Hosting I/O Usage, IOPS Limit And Entry Processes Limit

While looking for an appropriate web hosting solution for your website, you will come across different web hosting packages that offer various configurations of storage space, monthly bandwidth, memory and CPU.

{% hint style="success" %}
This article originally from [milesweb.com](https://www.milesweb.com/hosting-faqs/all-you-need-to-know-about-web-hosting-i-o-usage-iops-limit-and-entry-processes-limit/)
{% endhint %}

However, apart from the standard factors, there are other important factors that you need to consider while signing up for a web hosting package, they are mentioned below:

* What is web hosting I/O usage?
* What does IOPS mean?
* What Is Entry Processes Limit?
* What Is Number Of Processes?

The factors mentioned above are extremely crucial considerations and usually, they are not mentioned by the web hosting companies to the end-users. You can only know about these factors when you purchase the web hosting package. You will be able to see the I/O usage, IOPS, entry processes and number of processes in your web hosting control panel only if your web host allows these specifications to be displayed.

At times, you can ask about these technical specifications during a pre-sales chat or email query; however, there will be few salespeople who know about them.

Some web hosting providers include these factors in technical information that is mentioned in a small print in the ‘Terms and Conditions’, ‘Fair Usage Policy’ or in the ‘Terms of Use’ section.

Let’s have a deeper look at these factors so that you can make a better decision about choosing the right web hosting solution.

## What Is Web Hosting I/O Usage?

The web hosting I/O usage refers to the disk input and output (I/O). The disk I/O speed specifies how fast the website or scripts are allowed to carry out the input and output operations per second on your hosting server. Therefore, when it comes to the I/O range, the more the better. When someone visits your website or when you send or receive an email, your hosting server is carrying out the I/O operations.

If your server is set on a low I/O speed, your website and scripts will always perform at a slow pace; irrespective of the storage space, bandwidth, CPU and RAM offered in your web hosting package. A slow hosting platform will make your website slow resulting in damaging the online reputation, it may lead to data loss and bad email communication.

Offering a higher I/O is expensive for the web hosting providers which are why they do not allow more than 1 MB/s disk I/O speed on a shared server.

**Benefits of having a higher I/O limit:**

* More read/write data can be executed on the disk.
* Useful for hosting videos and downloading or streaming on the website.
* Enables large scripts to run faster.
* Helps in the execution of large database queries and operations.
* Prevents website freeze or slow loading of a website with heavy scripts

## What Does IOPS Mean?

Similar to the I/O speed, IOPS refers to Inputs Outputs Per Second. IOPS determines the speed at which a hard drive reads data from and writes data to a hard drive. IOPS is used for both traditional spinning hard drives and SSD drives. For instance a 7.2k SATA drive contains about 80 IOPS. When it comes to a hosting server, even though some web hosting providers provide SSD hosting, but in reality they limit the IOPS for every account to a certain value. The most important fact for you to know is that the higher the IOPS, the faster your website will be.

## What Is Entry Processes Limit?

An 'Entry Process' denotes the number of PHP scripts running at a single time. An entry process usually takes approximately 1 second to complete, this is the reason why most of the people confuse the entry process with the number of visitors they can have on their website. If the entry process limit is 30, it does not mean that only 30 people can visit your website at once because the possibility of all the people accessing your website at the same second will not happen unless you have a very busy website.

The processes like cron jobs, shell scripts and other commands also utilize one entry process for the time duration when they are running.

If you plan to host multiple websites on a single hosting server, a higher entry process limit will surely help a lot.

**Benefits of higher entry processes limit:**

* Helps in catering to large website traffic.
* Has the ability to run more scripts at one time.
* Makes the website faster, especially in case of an ecommerce website where there are many PHP scripts running for database queries.
* Prevents your website from getting suspended with high traffic spikes.
* Important for WordPress multi-site build or for running multiple web applications in one hosting account.

## What Is Number Of Processes?

A standard shared server is limited to 25 simultaneous processes per cPanel. Most of the websites work perfectly with 25 concurrent processes limit. The website processes open and close so quickly that they can hardly overlap. These concurrent processes consist of IMAP, SSH connections and other processes running in the same account.

These processes are same as the entry processes; the only difference is that these processes include all the processes generated by the account or website apart from the specific page, SSH or cron jobs. If the number of processes has been crossed, error 500 or error 503 will be displayed when the website is accessed.

It is beneficial if you have many concurrent users connected to the same server at a given point of time for executing various processes like for examples for accessing emails through IMAP, FTP etc.

**Conclusion**

The information mentioned above will provide you better knowledge about the important aspects that you should look for in a web hosting package apart from the standard attributes. Don’t fall for unlimited storage and bandwidth unless you are aware of these important technical factors. If you are promised unlimited space but in reality if your website is getting limited, then there is no point in opting for such a web hosting plan.


