View Complete Thread
  • DropPhone DropPhone

    360 Posts

    Microsoft

    Moderator

    Re: PHP 7.2 and future of WinCache

    Sep 12, 2017 09:27 PM|DropPhone|LINK

    DRUE

    Out of curiosity, what would happen if opcache.enable_file_override and reroute_enabled were both on?

    Good question!

    The way PHP Core lets developers hook functions is by chaining to the existing value.  In this case, Zend Opcache is special, in that it's a zend_extension, and therefore gets first/final crack at all the hooks.  Which means Zend Opcache is the last one to hook things, which ensures it's always called first when a hooked function is called.

    If both WinCache and Zend Opcache are hooking the Exists() function, it would look like this:

    [calling script]
    |
    | -- zend_opcache.Exists | |-- WinCache.Exists | |-- PHPCore.Exists
    1. The calling script would call Exists, which redirects to zend_opcache.Exists
    2. If item is in Zend Opcache's cache, call returns, otherwise, Zend Opcache calls Exists, which redirects to WinCache.Exists
    3. If item is in WinCache's cache, call returns. Otherwise, WinCache calls Exists, which redirects to PHPCore.Exists

    Here's the thing: Zend Opcache's cache and WinCache's file cache contain different, but overlapping, items.  Zend Opcache's cache contains only *.php script files.  WinCache's file cache contains anything that was opened via the PHP streams interface. 

    So, let's walk through a couple of examples:

    A script starts executing.  PHP Core calls Compile script (remember, it's an interpreted language!  PHP must call compile every single time a script is executed in order to get the array of Intermediate Language opcodes).  The Compile function is actually hooked by Zend Opcache, which checks to see if the opcode array for the script was previously cached.  In this example, it hasn't.  Zend Opcache calls the PHP Core compile, which reads the *.php script file via the streams interface, which WinCache has hooked.  WinCache checks to see if the stream exists in its file cache (which, for this example, it hasn't).  WinCache calls the PHP Core stream APIs, and caches the result.  PHP Core parses, compiles, optimizes, and returns the opcode array to Zend Opcache, which caches it. Then, PHP Core executes the script.

    Second example:

    A script calls Readfile('foo.txt'), which is actually hooked by WinCache.  WinCache.Readfile checks to see if the file exists in its cache.  If it does, it returns it.  If it hasn't, it calls the PHP Core Readfile('foo.txt'), and caches the result before returning to the script.

    Third example:

    A script calls file_exists('foo.txt'), which redirects to Zend Opcache's file_exists.  Zend Opcache looks to see if it has the file cached.  It won't, because it's not a *.php file, so it calls the next file_exists(), which redirects to WinCache's file_exists.  If WinCache has the file cached, it will return TRUE.  If it's not cached, it calls the PHP Core file_exists().  NOTE: Neither WinCache nor Zend Opcache maintain a "negative cache"!  Which means that if PHP Core file_exists() returns FALSE, neither WinCache nor Zend Opcache will cache that FALSE result.  This is because there are many, many ways for a script to create a file that it becomes prohibitive to hook all the different ways and 100% accurately detect when a file is created.  So, do yourself a favor, and try not to call file_exists() all over the place for files you know don't exist.  It's painful, and hits every component that hooks the file_exists() function before eventually hitting the file system.

    I hope this helps!

        --E.