Skip to content

Latest commit

 

History

History

boilerplate-v3

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

Boilerplate plugin class for phpMussel v3.

By the fact of your reading of this text and reviewing this boilerplate, I would assume that you're already reasonably familiar with phpMussel v3 from the standpoint of basic installation, usage, and structure. If such is the case, working with this boilerplate should be reasonably straightforward and easy for you. But, if not, I would recommend familiarising yourself with those things first, because going any further with this boilerplate could be potentially confusing and difficult otherwise.

Other than this README, you should see three things in this directory:

  1. An "assets" directory.
  2. An "l10n" directory.
  3. An "src" directory.

The "src" directory is intended for any PHP classes compatible with Composer's autoloader (PSR-4/PSR-4). For the purpose of this boilerplate, it contains just one class:

phpMussel\BoilerplateV3\BoilerplateV3

The "l10n" directory is intended for any L10N data needed by the plugin. This includes descriptions for any attached configuration directives to be utilised by the front-end. It should follow the same structure utilised by other phpMussel components.

The "assets" directory is intended to contain any other data needed by the plugin (with the exception of any additional third-party dependencies, which should be automatically placed into their own directories by Composer, and should preferably not become the responsiblity of the plugin itself). For the purpose of this boilerplate, it contains just one file: "config.yml". That file tells phpMussel about the configuration directives needed by the plugin, which allows phpMussel to populate any needed fallbacks for that configuration and to display them correctly at the front-end configuration page.

To see the existing boilerplate code in action, the implementation will need to use the events orchestrator to instantiate the boilerplate itself as an event handler at the "atEndOf_scan" event.

As an example:

<?php
// Path to vendor directory.
$Vendor = __DIR__ . DIRECTORY_SEPARATOR . 'vendor';

// Composer's autoloader.
require $Vendor . DIRECTORY_SEPARATOR . 'autoload.php';

$Loader = new \phpMussel\Core\Loader();
$Scanner = new \phpMussel\Core\Scanner($Loader);
$FrontEnd = new \phpMussel\FrontEnd\FrontEnd($Loader, $Scanner);
$Web = new \phpMussel\Web\Web($Loader, $Scanner);

// This line here is where we're doing what I described above.
$Loader->Events->addHandler('atEndOf_scan', new \phpMussel\BoilerplateV3\BoilerplateV3($Loader));

// Scans file uploads (execution terminates here if the scan finds anything).
$Web->scan();

The boilerplate code in this case works by taking advantage of the __invoke magic method provided by PHP. However, you can also add new event handlers by using closures (anonymous functions), which usually would be defined at the constructor (but can be defined wherever you want, as long as that point in your codebase is otherwise executed at some point, in order to have the event handler added during execution). The default logging mechanism in phpMussel does it that way.

An excerpt from Scanner.php (the phpMussel core):

        /**
         * Writes to the standard scan log upon scan completion.
         *
         * @return bool True on success; False on failure.
         */
        $this->Loader->Events->addHandler('writeToScanLog', function (): bool {
            /** Guard. */
            if (
                strlen($this->Loader->ScanResultsFormatted) === 0 ||
                !$this->Loader->Configuration['core']['scan_log'] ||
                !($File = $this->Loader->buildPath($this->Loader->Configuration['core']['scan_log']))
            ) {
                return false;
            }

            $Results = sprintf(
                "%s %s\n%s%s %s\n\n",
                $this->Loader->InstanceCache['StartTime2822'],
                sprintf($this->Loader->L10N->getString('grammar_fullstop'), $this->Loader->L10N->getString('started')),
                $this->Loader->ScanResultsFormatted,
                $this->Loader->InstanceCache['EndTime2822'],
                sprintf($this->Loader->L10N->getString('grammar_fullstop'), $this->Loader->L10N->getString('finished'))
            );

            if (!file_exists($File)) {
                $Results = \phpMussel\Core\Loader::SAFETY . "\n" . $Results;
                $WriteMode = 'wb';
            } else {
                $WriteMode = (
                    $this->Loader->Configuration['core']['truncate'] > 0 &&
                    filesize($File) >= $this->Loader->readBytes($this->Loader->Configuration['core']['truncate'])
                ) ? 'wb' : 'ab';
            }

            $Handle = fopen($File, 'ab');
            fwrite($Handle, $Results);
            fclose($Handle);
            $this->Loader->logRotation($this->Loader->Configuration['core']['scan_log']);
            return true;
        });

If you want, you can append new event handlers to the existing stack for a given event, or replace the entire stack with just your new event, or just outright delete the entire stack, or event protect the stack against other, future additions which might otherwise occur at some point in the execution chain. To understand how that works, you can read the documentation for the events orchestrator.

In theory, it should generally be possible to extend phpMussel v3 as to be able to do almost anything which is conceivably possible to do in PHP.

Some ideas about the kinds of things you might want to do:

  • Use Monolog to replace phpMussel's existing logging mechanisms, in order to integrate phpMussel more closely with the logging architecture at your existing projects and apps.

Example:

<?php
use Monolog\Logger;
use Monolog\Handler\FirePHPHandler;
use phpMussel\Core;

$Logger = new Logger('phpMussel_logger');
$Logger->pushHandler(new FirePHPHandler());

$LoggerEvent = function ($Data) use (&$Logger) {
    $Logger->info($Data);
}

$Loader = new Loader();
$Scanner = new Scanner($Loader);

// Replace the existing event stack for writeToScanLog with our Monolog logger.
$Loader->Events->addHandler('writeToScanLog', $LoggerEvent, true);

// Destroy event stacks that we don't need anymore (because we're relying on
// Monolog now, and we're only interested in logging scan events).
$Loader->Events->destroyEvent('writeToSerialLog');
$Loader->Events->destroyEvent('writeToUploadsLog');
$Loader->Events->destroyEvent('writeToPHPMailerEventLog');

Warning: I haven't tested the above example, and of course, it's only an example, and would likely need to be heavily modified as per your exact needs regardless.

Currently supported events:

Event Description
afterChameleonDetections Fired immediately after chameleon attack detections finish for the current scan target.
afterURLScanner Fired immediately after the URL scanner finishes processing the current scan target.
afterVirusTotal Fired after all Virus Total API lookups have finished for the current scan target.
atEndOf_scan Fired immediately before the scanner returns the scan results.
atStartOf_archiveRecursor Fired right at the very beginning of a new archive recursor iteration.
atStartOf_dataHandler Fired right at the very beginning of a new dataHandler iteration.
atStartOf_metaDataScan Fired right at the very beginning of a new metadata scan iteration.
atStartOf_normalise Fired immediately before phpMussel attempts to normalise data.
atStartOf_quarantine Fired right at the very beginning of a new quarantine iteration.
atStartOf_recursor Fired right at the very beginning of a new recursor iteration.
atStartOf_scan Fired right at the very beginning of a new scan operation.
atStartOf_web_scan Fired immediately when the upload handler's scan method executes.
beforeChameleonDetections Fired immediately before chameleon attack detections begin for the current scan target.
beforeOutput Fired immediately before the upload handler sends page output, and only if there's output to send (i.e., if nothing was blocked, or if the template files are missing, it won't fire).
beforeSigFile Fired right at the very beginning of the new iteration of any signature file for the current scan target.
beforeSigFile Fired right at the very beginning of the new iteration of any signature file for the current scan target.
beforeSigFiles Fired immediately before any particular given signature file class begins iterating for the current scan target.
beforeURLScanner Fired immediately before the URL scanner begins processing the current scan target.
beforeVirusTotal Fired immediately before Virus Total API lookups occur for the current scan target.
before_scan Fired during recursor iterations, just before it begins processing the current scan target.
countersChanged During any particular scan operation, phpMussel tracks how many files it has finished scanning, and tracks how many files there are remaining to be scanned. Whenever these numbers change (e.g., a new file is found, or it finishes scanning a file), this event is fired (and will most likely fire multiple times during the scan operation).
error Fired whenever an error, warning, or notice occurs.
final Fired when the loader is destroyed.
frontend_before_page Fired when the front-end view method executes, after preparing some basic variables needed for displaying front-end pages, but before preparing any page output (available only if the phpMussel front-end is installed).
sendMail Fired whenever phpMussel tries to send an email (e.g., to send notifications about blocked uploads, and to send two-factor authentication codes to front-end users).
writeToPHPMailerEventLog Fired whenever the phpMussel-PHPMailer linker is invoked, after it has finished attempting to send an email (whether successfully, unsuccessfully, or otherwise), and is ready to log the results (available only if the phpMussel-PHPMailer linker is installed).
writeToScanLog Fired when the outermost recursor has finished and phpMussel is ready to log the results.
writeToSerialLog Fired when the outermost recursor has finished and phpMussel is ready to log the results.
writeToUploadsLog Fired when the upload handler has finished scanning uploads and is ready to log the results, but before sending any page output, and only if an upload was blocked.

New events may be added in the future, and may be added upon request. If you would like to see additional events added, you are welcome to create a new issue to let us know about it. :-)


Last Updated: 19 July 2020 (2020.07.19).