Creating Extras for Revo 3 Part 7: Extending Processors and Other Objects

Extending processors and other objects in the code of your Extra.

By Bob Ray  |  June 20, 2022  |  8 min read
Creating Extras for Revo 3 Part 7: Extending Processors and Other Objects

In the previous article, we looked at how to deal with Media Source objects in the code of your Extra so that it will run in both Revo 2 and Revo 3. In this one, we’ll look how to extend MODX processors and other objects in code intended only for Revo 3. In the next article, we’ll see a way to do it that will work in both Revo 2 and Revo 3. We’ll look at processors first.

New Package for Revo 3

If your package will run only in Revo 3, you have several choices:

First, you can get rid of any processors. This is seldom a real option.

Second, create your own processor that either doesn’t extend anything or extends your own base processor. This is the way the Login Extra was originally written for Revo 2.

Third, extend one of the existing MODX processor classes.

This is usually the best option, but it means re-writing your processors if you want your code to survive an upgrade to Revo 4 (or whatever it’s called) without “including” the core/include/deprecated.php file, and letting MODX generate a bunch of deprecation log entries.

The deprecated.php file provides the aliases for the Processor classes, so most (but not all) Revo 2 code with processors will still run in Revo 3. There is no modProcessor class in Revo 3.

The base class for processors in Revo 3 is just Processor. If you want to use that class as the parent of your class, You can do it either of these ways:

/* Near the top of the file with your processor class */
use MODX\Revolution\Processors\Processor;

/* In the class declaration */
class MyProcessor extends Processor {
    /* Your processor code here */
}

Or, you can skip the use statement and do this:

class MyProcessor extends MODX\Revolution\Processors\Processor {
    /* Your processor code here */
}

I don’t think performance will be significantly affected by the choice, so it’s largely a matter of personal preference. The second choice above helps keep you from having to scroll up to see the full namespaced path. It also keeps you from having to use the ::class suffix where the fully qualified, namespaced path is required. It may speed things up by a few milliseconds during compile-time, since will skip the storage of the use alias and the translation of the abbreviated fully qualified class name.

On the other hand, many coders think having the use statements at the top makes your code easier to read. The class name is much shorter and less likely to wrap, and you can see at a glance at the top of the code what objects are used below. In addition, many code editors can make use of the ::class when alerting you to errors and showing the class name during debugging. This is the way it’s done in most of the Revo 3 core code.

Options for Processor Classes to Extend

In the code above, we extended the Processor class, which is the base class for most of the processor classes in Revo 3. There are other options though. In the examples below, we’ll use the fully qualified class name. If you prefer to have use statements, you can work them out for yourself. Just put the fully qualified name in the use statement, and shorten the class name to its final term (with no backslashes).

The Processor class (see code above) is an abstract class, so can’t be instantiated. It implements the following methods:

__construct() /* sets $this->modx and calls setProperties() */

/* sets $this->path() */
setPath(string $path)

/* sets $this->properties; merges them with existing ones if $merge is true */
setProperties(array $properties, bool $merge = true)

/* Unsets the specified property */
unsetProperty(string $property)

/* Just returns true unless you override it in your class */
checkPermissions()

/* Just returns true unless you override it in your class */
initialize()

/* Returns an empty array unless you override it */
getLanguageTopics()

/* Returns $this->modx->error->success($msg,$object); */
success(string $msg = '', mixed $object = null)

/* returns $this->modx->error->failure($msg,$object); */
success(string $msg = '', mixed $object = null)

/* Returns $this->modx->error->hasError() */
hasErrors()

/* Calls $this->modx->error->addField($key,$message); */
addFieldError(string $key, string $message = '')

/* Returns a Processor object with the specified class name
  can be called with Processor::getInstance() */
static getInstance(modX $modx, string $className, array $properties = [])

/* Calls initialize(), checkPermissions(), and process() */
run()

/* Sets a property */
setProperty(string $key,mixed $value)

/* Gets the value of a property if it exists or default value if not */
getProperty(string $k, mixed $default = null)

/* Returns 0, 1, or NULL (see code) */
setCheckbox(string $k, bool $force = false)

/* Returns $this->properties */
getProperties();

/* Merges properties with existing ones updates $this-properties,
   and returns it */
setDefaultProperties(array $properties = [])

/* Converts a PHP array to JSON. Returns JSON array with success, count, and
   results members. Useful for AJAX processors and list processors */
outputArray(array $array, int | bool $count = false)

/* Converts $data to JSON and returns it */
toJSON(mixed $data)

/* Used internally to encode JSON literals in a JSON string
   &$value is a reference so no return */
_encodeLiterals(&$value, $key)

/* Used internally to reverse _encodeLiterals() */
_decodeLiterals(string $string)

If you extend the Processor object, your code must implement a process() method with no arguments. Often, you will also implement initialize(), and getPermissions(), though it's not required.

ModelProcessor

This is another option to use for extending your processor:

class MyClass extends MODX\Revolution\Processors\ModelProcessor {
    /* Your processor code here */
}

You will have all the methods of the Processor class and two additional methods in the ModelProcessor class, which will override the ones in the Processor class:

/* To call this, you must first set $this->permissions = 'somePermission'.
   In spite of its name, it checks a single permission and returns true
   or false depending on whether the current user has that permission */
checkPermissions()

/* Simply returns $this->language topics, which you're required to set */
getLanguageTopics()

Generally, you need to implement code that uses these two, since they’re not very useful as written, or you need to extend another processor that’s a descendant of this class.

There are three more processor classes in the same location as the ones above. The DriverSpecificProcessor class extends the Processor class and just overrides the getInstance() method. It’s used in some MODX database classes. The ProcessorResponse and ProcessorResponseError classes are used as is by processor classes and are generally not used as parents of other classes (though they could be if you have a special use case).

More Specific Processors

You may want to create a processor that extends a more specific processor class like GetListProcessor, createProcessor, removeProcessor, etc., or use it as is. The generic abstract base classes can be found in the core\src\Revolution\Processors\Model\ directory. You’re more likely, though to want the processors that are tailored for specific objects. Most of them extend the base processors just mentioned. A few extend the Processor and ModelProcessor classes.

For example, the Resource GetList processor found in the core\src\Revolution\Processors\Resource\ directory. They can all be found in the core\src\Revolution\Processors\ directory.

In spite of their absolute path, the fully qualified name of the classes will be either of these:

MODX\Revolution\Processors\{objectName}\{ProcessorName}
MODX\Revolution\Processors\{subdirectory(s)}\{objectName}\{ProcessorName}

Here are some examples of the first form:

MODX\Revolution\Processors\Resource\GetList
/* media Source */
MODX\Revolution\Processors\Source\Update
MODX\Revolution\Processors\Context\Remove

Here are some examples of the second form:

MODX\Revolution\Processors\Element\Template\Duplicate
MODX\Revolution\Processors\Element\Snippet\Create
MODX\Revolution\Processors\Security\User\GetList
MODX\Revolution\Processors\Security\User\Create
MODX\Revolution\Processors\Browser\File\Create
MODX\Revolution\Processors\Workspace\Packages\GetList
MODX\Revolution\Processors\Workspace\Packages\Rest\GetList
MODX\Revolution\Processors\Model\CreateProcessor
MODX\Revolution\Processors\Element\Category\Remove
MODX\Revolution\Processors\System\Dashboard\GetList
MODX\Revolution\Processors\System\Dashboard\Widget\Remove
MODX\Revolution\Processors\System\Menu\Create

It can be devilishly difficult to come up with the fully qualified class name of a processor class you want to extend. Hopefully, the examples above will help.

Other Objects

This is simpler, because the classes you want to extend generally exist, but with a different fully qualified class name, with the exception of Media Sources, Transport Packages, and Mail, which simply require a different prefix and are covered in other articles. Here’s an example:

use MODX\Revolution\modUser;
use MODX\Revolution\modResource;
use MODX\Revolution\modSnippet;

class MyUser extends modUser() {
}
class MyResource extends modResource() {
}

class MySnippet extends modSnippet() {
}

  /* or without the use statement */
class myUser extends MODX\Revolution\modUser {
}
class MyResource extends MODX\Revolution\modResource {
}

class MySnippet extends MODX\Revolution\modSnippet {
}

Single Package for Revo 2 and Revo 3

Unfortunately, use statements can’t be used in the normal way for code that’s meant to run in both Revo 2 and Revo 3. That’s the reason for the “class prefix” we’ve seen in previous articles. The bad news is that even that won’t work for classes that extend an existing MODX class (including modProcessor).

The solution for that is complex enough that it deserves its own post. In the next article, we’ll see that solution, along with code for extending a processor class, showing how to extend a MODX class in a way that will work in both Revo 2 and Revo 3.


Bob Ray is the author of the MODX: The Official Guide and dozens of MODX Extras including QuickEmail, NewsPublisher, SiteCheck, GoRevo, Personalize, EZfaq, MyComponent and many more. His website is Bob’s Guides. It not only includes a plethora of MODX tutorials but there are some really great bread recipes there, as well.