The version on the dev branch this week, ProcessWire 3.0.111, focuses primarily on refactoring and improving some of the core Pages classes. Over time I've found an occasional redundancy here and there (like two blocks of code that are accomplishing similar things) that wasn't recognized when originally coded, leaving room for an optimization. I like to iron those things out by refactoring the code in question and ensuring that any redundancies are resolved. While it's not the most interesting thing to write about, I do think it's an important part of maintaining the core codebase. Maintaining that codebase and keeping it at the highest quality is one of the most important parts of my job, and one that I really enjoy.
I don't think we will ever go through a major rewrite of the software, like we see some other projects do. The core is really solid and I prefer to refactor, improve or optimize parts of it when I find something that would benefit from it, either in terms of its output, efficiency, or maintainability. There's always room for improvement, but I feel like we got the foundation right. It's something that we can continuously build from.
On the other hand, some projects like to do a full rewrite every few years. But it seems like that's when things start to get volatile—the familiar, trusted and stable tool becomes something that seems like it's more about the product (or goals of the company behind it), and less about your own projects that you use it for. So in case you ever wondered if we'd do that, the answer is no. Instead, we'll just keep getting better every day as one of your favorite tools—slowly but surely, and stable as always, focused on being a reliable and enjoyable tool to build and power your projects.
What I mentioned above hasn't always been the case. Technically, ProcessWire is the 3rd major rewrite of Dictator CMS, started back in 2003. The 2nd major rewrite was ProcessWire 1.x (in 2006), and the 3rd (2010) was the start of the ProcessWire that all of us know and use today. It can take a couple tries to get things right, and that was the case with ProcessWire. Luckily all that iteration happened before ProcessWire became an open source project with lots of users all over the globe. Now eight years since ProcessWire 2.x was introduced, I still feel like the foundation is every bit and solid and where it should be, and it's been getting incrementally better nearly every single day since then. The strong foundation combined with the modular approach means we don't ever have to start over, and instead can focus on the tool that's here, making it better and better, and having fun in the process.
That's what I worked on this week (like many weeks)—refactoring a few things that I've been doing in bits and pieces over the last month, and committed them to the core for version 3.0.111. Below are some more details.
New PagesNames class
There is a lot of logic in ProcessWire for generating page names, handling name collisions, pages not-yet-named, etc. All of this logic was rewritten and moved into its own class called PagesNames. While it's primarily for internal use by the core Pages class (represented by the $pages API variable), it has some new methods in it that some may also find handy on occasion.
You can obtain the runtime instance of PagesNames by accessing
$pages->names(). Since the methods in there are primarily for use by the core, I'm not going to cover them in detail here, other than just give an example of one of them. Lets say you were using the API to create pages, and you wanted to give the page a name that you knew would be guaranteed unique, globally:
$page->name = $pages->names()->uniquePageName('test');
If there was no page named "test" anywhere on your site, then it would return "test". If there was a page with that name already, it would return an incremented version of it, i.e. "test-1". Or if you want it to come up with the globally unique page name (without being based on anything) you could just call it like this:
$page->name = $pages->names()->uniquePageName();
If you only needed the page's name to be unique within a particular parent, then you'd want to provide it with a copy of the $page you will be populating it to, optionally in addition to your preferred name:
$page->name = $pages->names()->uniquePageName('test', $page);
This uniquePageName() method is just one example of several methods available in $pages->names(). But chances are you won't need these very often, because these methods were actually added for the purpose of having the core more efficiently automate this stuff for you, when the conditions allow.
Another class that went through a refactoring is the PagesTrash class, which provides the trash(), restore() and emptyTrash() methods for the $pages API variable. These methods also translate directly to actions available in the UI. I noticed some redundancies that needed to be ironed out here, so went ahead with doing that. It also improves the trash emptying process and enables the restore() action to restore pages that it previously couldn't (due to name collisions, which it now resolves).
New WireRandom class
ProcessWire's Password class makes use of some cryptographically secure random string and number generation functions. As needs for random generators increased, I've lately found myself loading instances of the Password class; not for anything related to passwords, but rather to access its random generator functions. And some recently added random generators went into that class as a result. That just didn't seem right. So all of those random generators have now been moved into their own dedicated class: WireRandom.
In combination with that, I've added several new kinds of random generators as well. Some of them are improvements upon what was previously in the Password class, while others are brand new. Included in the class you'll find functions for generating random integers, alphanumeric strings, alpha-only strings, numeric-only strings, base64 strings, passwords, shuffling arrays or strings, plucking values or keys randomly from arrays, and more. All of these are cryptographically secure random generators so long as the PHP version is 7.x and above OR the mcrypt functions are available in PHP. Many of the random generator functions provide comprehensive options for customizing the output.
If you want to use any of these random generator functions, you'll need to obtain an instance of the WireRandom class:
$random = new WireRandom();
…and then you can use it like in these examples:
// get a random integer between 0 and 255 $i = $random->integer(0, 255); // get a random alphanumeric string 10 characters long $s = $random->alphanumeric(10); // get a random alpha string 20 characters long $s = $random->alpha(20); // get a random numeric string 5 characters long $s = $random->numeric(5); // get a random value from the array $v = $random->arrayValue(array('a','b','c','d','e','f'));
Note: if you were using any random generator functions from the Password class, they will still work, as the class now delegates to them automatically to the WireRandom class.
These are just a few examples of updates put in place this week, and while they are more about refactoring what's already present than on introducing new things, they are an important part of maintaining a quality codebase. It emphasizes our process of continuous improvement on a strong foundation with stability, security and reliability. Thanks for reading and enjoy reading the ProcessWire Weekly this weekend!
Your e-mail is kept confidential and not included with your comment. Website is optional.