ProcessWire 3.0.137 core updates

Core version 3.0.137 on the dev branch adds the ability to hook multiple methods at once, in a single call. This post details how it works and provides a useful example of how you might put it to use in your development environment. This version also refactors and improves upon several core classes.

This latest version on the dev branch makes improvements to several classes, but most of it is code maintenance and improvement. For instance, several related methods from the Pageimage class were moved to their own class so that those methods can be loaded on demand as needed. Another example is that the jQuery AsmSelect plugin went through a significant refactor as part of maintaining the code and documentation (that particular plugin was even part of ProcessWire 1.x). There were a few other code maintenance tasks like this.

There is also one small but [I think] really useful update that I want to highlight from 3.0.137, which is the ability to hook multiple methods in one call. In this post we’ll take a quick look at how it works, and then we’ll look at an example that you might find useful in your development environment.

Hooking multiple methods at once

ProcessWire 3.0.137 adds the ability to hook multiple methods to the same handling function at once. While you could do this before (with multiple addHook calls), it was an inconvenient and a potentially confusing way to go. Now it’s very simple, just separate the methods you want to hook with a comma:

// hooking to viewable and editable methods from all Page instances
$wire->addHookAfter('Page::viewable, Page::editable', function($e) {
  $event->message("Called the $e->method hook");

// hooking to saveReady and saved from $pages API var
$pages->addHookBefore('saveReady, saved', function($event) {
  $event->message("Called the $event->method hook");

This works with any of the addHook methods, whether it’s addHookBefore, addHookAfter, addHookProperty, addHookMethod, or just the regular addHook method. It’s so simple that it hardly warrants a blog post on its own, but it's so useful that I also didn't just want to casually mention it in a forum post either. Below is a real life example of using this capability that I think you might find useful.

On-demand mirroring of remote web server files to your dev environment

Some of the projects that I work on involve ProcessWire powered websites that have huge amounts of file assets in /site/assets/files/... more than I would want to spend time mirroring to my local dev environment (or anywhere else I might keep a separate copy of the site). We're talking about gigabytes of assets that might take hours or an entire day to transfer, not to mention fill up the SSD on my computer.

When I make a copy of such sites to work on in my development environment, I just copy the /site/templates/ directory and database (and site modules, if needed), and don't bother with the trying to copy all those file assets to my computer. But because the /site/assets/files/* are an important part of these websites (images in particular) it makes it difficult to adequately test things out in my dev environment without having those assets locally (lots of broken images for starters).

What I've found works very nicely for situations like this is to copy the assets from the live server automatically, on-demand, as needed. So any page I view on the front-end (or edit on the back-end) has all the assets it needs, but only upon request. Because I don't need to test out all of the thousands of pages on the website, I don't need all the assets from them either... just the assets for the pages I'm developing and testing for.

This is accomplished with a hook in the /site/ready.php file in your development environment, like this below:

$wire->addHookAfter('Pagefile::url, Pagefile::filename', function($event) {

  $config = $event->wire('config');
  $file = $event->return;

  if($event->method == 'url') {
    // convert url to disk path
    $file = $config->paths->root . substr($file, strlen($config->urls->root));

  if(!file_exists($file)) {
    // download file from source if it doesn't exist here
    $src = '';
    $url = str_replace($config->paths->files, $src, $file);
    $http = new WireHttp();
    $http->download($url, $file);

Note that the above hooks both the url and filename methods from the Pagefile class (which also implies the Pageimage class) in a single addHook statement. This requires ProcessWire 3.0.137, though can be duplicated on earlier versions by using two separate addHook() calls and providing the implementation as a separate independent function or method.

What it does is hook after every url() and filename() call to an image or file field, and if the file does not exist locally, it attempts to download it from the remote server. That way, any request for a file or image from ProcessWire ends up returning something that can actually be displayed, resized and manipulated, rather than a 404 or other error. But because the assets are only downloaded on-demand, you don't end up filling your development environment with hundreds of gigabytes of unnecessary files... just the ones you need to develop and test.

Another strategy I've used is to hook after Page::render() and replace all local URLs with remote URLs. That way we are just pointing to the remote assets rather than maintaining copies of them. But I've found that this doesn't work nearly as well because ProcessWire expects the files to be accessible on the file system so that it can read file attributes (size, width, height, date) as well as create variations from images through size() calls and more. So URL replacement is not nearly as useful because you still end up with lots of errors from function calls that can't do their job because of missing files. Whereas, I've found the above method enables you to exactly reproduce the result you would get on the live server. The only real downside is that you may wait a few extra seconds for a page to render the first time it needs to download the files for that request, a small price to pay for the convenience.

Once you've finished your work on the site, it's perfectly fine to delete everything in /site/assets/files/ on your development server if you want to. That's because the next time you work on the site (and load it in your browser), it will again download everything it needs to fulfill the page requests on-demand.

CKEditor update and new Debug::backtrace() method

A recap of 3.0.136— We didn't have a blog post last week and instead I posted the updates for 3.0.136 in the forum. I'm repeating that update here for those that didn't see it yet.

ProcessWire 3.0.136 upgraded the version of CKEditor from 4.10.1 to 4.12.1. While that might look like a minor version bump, it’s actually 5 versions ahead and includes quite a lot of new features, changes and fixes. See the CKEditor release notes for more details on all that's been added and changed in the last 5 versions.

It was a year or two ago when it seemed like CKEditor was going to be phasing out CKE 4 in favor of CKE 5. But interestingly it now seems like there is a new focus in CKE 4 from the CKEditor folks, so I’m going to have to start watching the version updates more often. Of course, I remain interested in CKE 5 too, but it’s nice to see CKE 4 (my favorite editor for a long time) going so strong and getting new features and attention, which is also a nice benefit for all of us PW users. I’m looking forward to working with some of the new stuff they’ve added in recent versions as well. For instance, the autocomplete feature sounds like it has nice potential for inserting Hanna codes or links to other PW pages, among other things.

Also new in 3.0.136 is a new Debug::backtrace() static method in the core. I often use PHP’s debug_backtrace() method when debugging (and it appears in PW’s fatal exceptions), but the reality is it gives me a lot of stuff I just don’t want… all the hook method calls and such that aren’t usually relevant to what I’m trying to find. So the new built in Debug::backtrace() method returns a much simpler array than PHP does, and it also excludes all of the [likely] irrelevant internal hook method calls, and is just generally more focused on what you are likely to need from a backtrace in ProcessWire. It’s very convenient to plug into a ProcessWire $wire->message(Debug::backtrace()), or a Tracy Debugger bd(Debug::backtrace()); or even an echo '<pre>' . print_r(Debug::backtrace(), true); call. Though please consider this new method a work in progress, as it’s just a start at the moment and is likely to get additional updates. At some point, PW’s fatal exceptions will likely use the output from this method as well.

Thanks for reading and I hope that you all have a great weekend and enjoy reading the ProcessWire Weekly!


  • Matt


    Thank you, Ryan! On-demand mirroring is a fantastic little helper. One of my sites has over 14GB of data in the files folder. This helps A LOT with my dev setup.

  • MrSnoozles


    • 5 years ago
    • 71

    Great updates again, Ryan.

    As for on-demand mirroring, what I usually do is to use .htaccess to rewrite the missing assets. E.g. you could add the following lines to your .htaccess (and replace with your live URL):

    # Rewrite missing images
    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
    RewriteRule (.*\.(gif|jpe?g|png|bmp))$$1 [NC,L]


NextWebP images on an existing site


In this post we’ll look at a process used for migrating an existing website to use WebP images. We’ll cover everything from preliminary considerations to implementation and testing, with lots of tips and tricks along the way. More 

Latest news

  • ProcessWire Weekly #511
    In the 511th issue of ProcessWire Weekly we'll cover the latest core updates, introduce a new third party module called Auto AVIF, and more. Read on! / 25 February 2024
  • Using date range fields in ProcessWire
    This week we'll take a detailed look at the newest addition to the ProFields set of modules: the Date Range Fieldtype and Inputfield.
    Blog / 24 November 2023
  • Subscribe to weekly ProcessWire news

“I am currently managing a ProcessWire site with 2 million+ pages. It’s admirably fast, and much, much faster than any other CMS we tested.” —Nickie, Web developer