Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions core/components/pdotools/docs/changelog.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
Changelog for pdoTools.

Unreleased
==============
- Fixed infinite recursion (memory exhaustion) when a plugin attached to the pdoToolsOnFenomInit event re-enters CoreTools::getFenom() while Fenom is being constructed (e.g. loading a static element with the pdoParser active). The event is now fired once from getFenom() after the Fenom instance is registered in the services container.

3.0.2-pl (11.04.2023)
==============
- Support for file snippets in the pdoPage class.
Expand Down
19 changes: 18 additions & 1 deletion core/components/pdotools/src/CoreTools.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ class CoreTools
protected $modx;
/** @var Fenom $fenom */
protected $fenom;
/** @var bool $fenomInitialized Whether the pdoToolsOnFenomInit event has been fired */
protected $fenomInitialized = false;
/** @var array $config Array with class config */
protected $config = [];
/** @var array $store Array for cache elements and user data */
Expand Down Expand Up @@ -146,7 +148,22 @@ public function getFenom()
$this->modx->services->add('fenom', new $class($this->modx, $this));
}

return $this->modx->services->get('fenom');
/** @var Fenom $fenom */
$fenom = $this->modx->services->get('fenom');

// Fire pdoToolsOnFenomInit only once, and only AFTER the instance has been
// stored in the services container. If the event were fired from the Fenom
// constructor (i.e. before the container memoizes the instance), a plugin
// attached to pdoToolsOnFenomInit that re-enters getFenom() - for example the
// pdoParser parsing media source properties while a static element is being
// loaded - would build another Fenom and recurse until the memory limit is
// exhausted.
if (!$this->fenomInitialized) {
$this->fenomInitialized = true;
$fenom->invokeInitEvent();
}

return $fenom;
}

/**
Expand Down
14 changes: 13 additions & 1 deletion core/components/pdotools/src/Parsing/Fenom/Fenom.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,24 @@ public function __construct(modX $modx, CoreTools $pdoTools)
$this->setOptions($options);

$this->_addDefaultModifiers();
}

/**
* Fire the pdoToolsOnFenomInit event so plugins can extend Fenom
* (register custom modifiers, functions, etc).
*
* This is intentionally NOT called from the constructor. It must be invoked
* by CoreTools::getFenom() AFTER the instance has been registered in the
* services container - otherwise a plugin attached to pdoToolsOnFenomInit
* that re-enters getFenom() would build another Fenom and recurse endlessly.
*/
public function invokeInitEvent()
{
$this->modx->invokeEvent(
'pdoToolsOnFenomInit',
[
'fenom' => $this,
'config' => $pdoTools->config(),
'config' => $this->pdoTools->config(),
]
);
}
Expand Down