Backup of a WordPress plugin / theme directory.

I often think: I’m just gonna do this little thing before and then I continue doing that.

The last little thing spanned a couple of days. I needed to make a patch for WordPress and cross fingers after pleading to apply it.

I could be wrong, but some years ago it was considered best practice to have users of your plugin to store their customizations into the same plugin directory. Well, maybe nobody advised me to but nobody advised me against either. The point is that it looks really neat to make users store all the stuff related to your plugin in the same place where your plugin leaves.

Years ago, however, there was no problem. Whenever you wanted to install a new plugin or update an old one, you always needed to first download it to your PC and then to upload it to your remote site, into the ´/wp-content/´ directory, possibly by means of an FTP client. The process was repetitive and cumbersome, but exactly by virtue of that you did things orderly.

Always make a backup of the stuff you are going to replace.

That was the mantra. Even on toilet walls you could read it.

Then, the update now link appeared and it all changed, for better. No more nothing, just a simple click and … magically everything happened remotely. Really really good feature. But there was a problem. A no-feature became a feature (bug?). The magic update now functionality never makes a backup of what is gonna replace. Why? I really don’t know. It is still a mantra for me!

So, the last couple of days, I made a patch for WordPress 4.1 and submitted it minutes ago. It’s quite simple: Always make a backup of the stuff you are going to replace. Then a plugin / theme developer can access a backup of their previous stuff and transparently migrate the previous custom files to the new version. For example, from an activation hooked function:

{[ .example | 1.hilite(=php=) ]}

Disable Xdebug for PHPUnit

If you do not need Xdebug when running tests with PHPUnit, then you can disable it and run your tests much faster. However, it’s not easy for me to remember to disable Xdebug, so in my TestCase class I’ve overridden ´setUpBeforeClass´ and ´tearDownAfterClass´ like this:

{[ .xdebug | 1.hilite(=php=) ]}

That only toggles the backtrace, so the speed gain is less impressive, but for me is an acceptable trade-off.

Ando_ErrorFactory

I’ve added a new class to my framework, Ando_ErrorFactory.

Here I use it to make PHP trigger an E_CORE_ERROR:

{[ .example | 1.hilite(=php=) ]}

-- s t a r t --
PHP Fatal error:  Class Oops must implement interface Traversable as part of either Iterator or IteratorAggregate in Unknown on line 0
PHP Stack trace:
PHP   1. {main}() /.../test1.php:0
PHP   2. Ando_ErrorFactory::E_CORE_ERROR() /.../test1.php:14
PHP   3. Ando_ErrorFactory->E_CORE_ERROR_by_declaring_a_class_directly_implementing_Traversable() /.../vendor/Ando/ErrorFactory.php:227
PHP   4. eval() /.../vendor/Ando/ErrorFactory.php:444

shutdown_error: E_CORE_ERROR (Fatal error)

error = Array
(
    [type] => 16
    [message] => Class Oops must implement interface Traversable as part of either Iterator or IteratorAggregate
    [file] => Unknown
    [line] => 0
)

Notice that the lines starting with “PHP” belong to STDERR (redirected to STDOUT, controlled by the ´error_reporting´ call), while the rest belong to the real STDOUT.

The take away is that we forcefully triggered an E_CORE_ERROR (fact confirmed by the STDERR portion) and our shutdown handler was able to log it (fact confirmed by the STDOUT portion). Given that such an error is a non catchable shutdown error, there is no other way to get a look at it than from inside a shutdown handler.

Here is what happens when instead we forcefully trigger an E_RECOVERABLE_ERROR:

-- s t a r t --

non_shutdown_error: E_RECOVERABLE_ERROR (Catchable fatal error)

error = Array
(
    [type] => 4096
    [message] => Object of class stdClass could not be converted to string
    [file] => /.../vendor/Ando/ErrorFactory.php(510) : eval()'d code
    [line] => 1
    [context] => Array
        (
             => echo new stdClass();
        )

)

#0  Ando_ErrorFactory::non_shutdown_error(4096, Object of class stdClass could not be converted to string, /.../vendor/Ando/ErrorFactory.php(510) : eval()'d code, 1, Array ( => echo new stdClass();)) called at [/.../vendor/Ando/ErrorFactory.php(510) : eval()'d code:1]
#1  eval() called at [/.../vendor/Ando/ErrorFactory.php:510]
#2  Ando_ErrorFactory->E_RECOVERABLE_ERROR_by_converting_to_string_an_object_of_a_class_without__to_string() called at [/.../vendor/Ando/ErrorFactory.php:294]
#3  Ando_ErrorFactory::E_RECOVERABLE_ERROR() called at [/.../tests/tmp2.php:22]

-- e n d --

shutdown_error: (none) (Unknown error)

The presence of the ´-- e n d --´ line in the output confirms, as expected, that E_RECOVERABLE_ERROR is not a shutdown error. Thanks to that, we had a chance to get to the backtrace from our non shutdown error handler, while the STDERR didn't get written to because we signaled to PHP we successfully handled the error by returning ´true´. In fact, here is what would happen if we returned ´false´:

-- s t a r t --

non_shutdown_error: E_RECOVERABLE_ERROR (Catchable fatal error)

error = Array
(
    [type] => 4096
    [message] => Object of class stdClass could not be converted to string
    [file] => /.../vendor/Ando/ErrorFactory.php(510) : eval()'d code
    [line] => 1
    [context] => Array
        (
             => echo new stdClass();
        )

)

#0  Ando_ErrorFactory::non_shutdown_error(4096, Object of class stdClass could not be converted to string, /.../vendor/Ando/ErrorFactory.php(510) : eval()'d code, 1, Array ( => echo new stdClass();)) called at [/.../vendor/Ando/ErrorFactory.php(510) : eval()'d code:1]
#1  eval() called at [/.../vendor/Ando/ErrorFactory.php:510]
#2  Ando_ErrorFactory->E_RECOVERABLE_ERROR_by_converting_to_string_an_object_of_a_class_without__to_string() called at [/.../vendor/Ando/ErrorFactory.php:294]
#3  Ando_ErrorFactory::E_RECOVERABLE_ERROR() called at [/.../tests/tmp2.php:22]

shutdown_error: E_RECOVERABLE_ERROR (Catchable fatal error)

error = Array
(
    [type] => 4096
    [message] => Object of class stdClass could not be converted to string
    [file] => /.../vendor/Ando/ErrorFactory.php(510) : eval()'d code
    [line] => 1
)

PHP Catchable fatal error:  Object of class stdClass could not be converted to string in /.../vendor/Ando/ErrorFactory.php(510) : eval()'d code on line 1
PHP Stack trace:
PHP   1. {main}() /.../tests/tmp2.php:0
PHP   2. Ando_ErrorFactory::E_RECOVERABLE_ERROR() /.../tests/tmp2.php:22
PHP   3. Ando_ErrorFactory->E_RECOVERABLE_ERROR_by_converting_to_string_an_object_of_a_class_without__to_string() /.../vendor/Ando/ErrorFactory.php:294
PHP   4. eval() /.../vendor/Ando/ErrorFactory.php:510

As you see, by returning ´false´, we told PHP that we didn't successfully handle the error and to continue its default course, which in this case, being E_RECOVERABLE_ERROR a shutdown error by default, consisted in shutting down the script. As a confirmation, notice the absence of the ´-- e n d --´ line in the output.

A fun fact I discovered while programming this class is that a fatal error is not necessarily a non-catchable shutdown error. In fact, both E_RECOVERABLE_ERROR and E_USER_ERROR are catchable shutdown errors. Likewise, E_PARSE, E_CORE_WARNING and E_COMPILE_WARNING are non catchable non shutdown errors. For each of these variations ErrorFactory has a corresponding method to return that set of errors.

{[ .example2 | 1.hilite(=php=) ]}

all_errors: Array
(
    [0] => E_ERROR
    [1] => E_WARNING
    [2] => E_PARSE
    [3] => E_NOTICE
    [4] => E_CORE_ERROR
    [5] => E_CORE_WARNING
    [6] => E_COMPILE_ERROR
    [7] => E_COMPILE_WARNING
    [8] => E_USER_ERROR
    [9] => E_USER_WARNING
    [10] => E_USER_NOTICE
    [11] => E_STRICT
    [12] => E_RECOVERABLE_ERROR
    [13] => E_DEPRECATED
    [14] => E_USER_DEPRECATED
)
catchable_errors: Array
(
    [0] => E_WARNING
    [1] => E_NOTICE
    [2] => E_USER_ERROR
    [3] => E_USER_WARNING
    [4] => E_USER_NOTICE
    [5] => E_STRICT
    [6] => E_RECOVERABLE_ERROR
    [7] => E_DEPRECATED
    [8] => E_USER_DEPRECATED
)
shutdown_errors: Array
(
    [0] => E_ERROR
    [1] => E_CORE_ERROR
    [2] => E_COMPILE_ERROR
    [3] => E_RECOVERABLE_ERROR
    [4] => E_USER_ERROR
)
non_catchable_errors: Array
(
    [0] => E_ERROR
    [1] => E_PARSE
    [2] => E_CORE_ERROR
    [3] => E_CORE_WARNING
    [4] => E_COMPILE_ERROR
    [5] => E_COMPILE_WARNING
)
non_shutdown_errors: Array
(
    [0] => E_WARNING
    [1] => E_PARSE
    [2] => E_NOTICE
    [3] => E_CORE_WARNING
    [4] => E_COMPILE_WARNING
    [5] => E_USER_WARNING
    [6] => E_USER_NOTICE
    [7] => E_STRICT
    [8] => E_DEPRECATED
    [9] => E_USER_DEPRECATED
)
catchable_shutdown_errors: Array
(
    [0] => E_RECOVERABLE_ERROR
    [1] => E_USER_ERROR
)
catchable_non_shutdown_errors: Array
(
    [0] => E_WARNING
    [1] => E_NOTICE
    [2] => E_USER_WARNING
    [3] => E_USER_NOTICE
    [4] => E_STRICT
    [5] => E_DEPRECATED
    [6] => E_USER_DEPRECATED
)
non_catchable_shutdown_errors: Array
(
    [0] => E_ERROR
    [1] => E_CORE_ERROR
    [2] => E_COMPILE_ERROR
)
non_catchable_non_shutdown_errors: Array
(
    [0] => E_PARSE
    [1] => E_CORE_WARNING
    [2] => E_COMPILE_WARNING
)

Note that the non_shutdown_error and shutdown_error handlers are included as a proof of concept. You are supposed to program yourself proper handlers, if and how you see fit. But you can certainly use them for testing something out, like I did above.