From fcbef362998ee70446352f3831450294811c9460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A4my=20Zehnder?= Date: Wed, 4 Oct 2023 18:09:56 +0200 Subject: [PATCH] Remove self-reference in `PelIfd::$maker_notes` `PelIfd::$maker_notes["parent"]` is always `$this` (there is only one caller of `PelIfd::setMakerNotes()`). However, adding `$this` to the array leads to a reference of a `PelIfd` instance to itself. As a result that `PelIfd` instance can not be cleaned up right away after all references to it go away (as the ref_count remains at 1 - the one to itself). Only at garbage collection time the garbage cycle is detected and that instance is cleaned up. This is an issue in case big images are processed one after another. In that scenario `PelIfd::$maker_notes['data']` contains the full image data (i.e. multiple MB) and is only released upon garbage collection time - which is not right away. Garbage memory is piling up and running out of memory is easily possible and the program might crash with `"Fatal error: Allowed memory size of xxx bytes exhausted"`. Removes the self-reference in the `PelIfd::$maker_notes` array. fixes: #6 --- src/PelIfd.php | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/PelIfd.php b/src/PelIfd.php index d57d7ddf..a98ca476 100644 --- a/src/PelIfd.php +++ b/src/PelIfd.php @@ -529,8 +529,6 @@ public function __construct($type) /** * Stores Maker Notes data for an IFD (Probably PelIfd::EXIF only). * - * @param PelIfd $parent - * the parent PelIfd of the current PelIfd * @param PelDataWindow $data * the data window that will provide the data. * @param PelIfd $parent @@ -539,10 +537,9 @@ public function __construct($type) * the offset within the window where the directory will * be found. */ - public function setMakerNotes($parent, $data, $components, $offset) + protected function setMakerNotes($data, $components, $offset) { $this->maker_notes = [ - 'parent' => $parent, 'data' => $data, 'components' => $components, 'offset' => $offset @@ -552,7 +549,7 @@ public function setMakerNotes($parent, $data, $components, $offset) /** * Returns the Maker Notes data for an IFD (Probably PelIfd::EXIF only). * - * @return array The maker_notes of IDF + * @return array The maker_notes of IFD */ public function getMakerNotes() { @@ -664,7 +661,7 @@ private function mapTagToIfdType(PelDataWindow $d, $offset, $tag, $components, $ } elseif ($tag == PelTag::MAKER_NOTE) { // Store maker notes infos, because we need PelTag::MAKE of PelIfd::IFD0 for MakerNotes // Thus MakerNotes will be loaded at the end of loading PelIfd::IFD0 - $this->setMakerNotes($this, $d, $components, $o); + $this->setMakerNotes($d, $components, $o); $this->loadSingleValue($d, $offset, $i, $tag); } return $ifdType; @@ -706,16 +703,17 @@ private function checkIfLoadingFinished() { if ($this->type == PelIfd::IFD0 && isset($this->sub[PelIfd::EXIF])) { // Get MakerNotes from EXIF IFD and check if they are set - $mk = $this->sub[PelIfd::EXIF]->getMakerNotes(); + $subIfd = $this->sub[PelIfd::EXIF]; + $mk = $subIfd->getMakerNotes(); if (! empty($mk)) { // get Make tag and load maker notes if tag is valid $manufacturer = $this->getEntry(PelTag::MAKE); if ($manufacturer !== null) { $manufacturer = $manufacturer->getValue(); - $mkNotes = PelMakerNotes::createMakerNotesFromManufacturer($manufacturer, $mk['parent'], $mk['data'], $mk['components'], $mk['offset']); + $mkNotes = PelMakerNotes::createMakerNotesFromManufacturer($manufacturer, $subIfd, $mk['data'], $mk['components'], $mk['offset']); if ($mkNotes !== null) { // remove pre-loaded undefined MakerNotes - $mk['parent']->offsetUnset(PelTag::MAKER_NOTE); + $subIfd->offsetUnset(PelTag::MAKER_NOTE); $mkNotes->load(); } }