Browse Source

Detect infinite recursion when initializing Table

Corey Taylor 5 years ago
parent
commit
ea0a7f5ed3

+ 15 - 0
src/ORM/Locator/TableLocator.php

@@ -48,6 +48,13 @@ class TableLocator implements LocatorInterface
     protected $_instances = [];
 
     /**
+     * List of Table classes being initialized.
+     *
+     * @var array<string, bool>
+     */
+    protected $initializing = [];
+
+    /**
      * Contains a list of Table objects that were created out of the
      * built-in Table class. The list is indexed by table alias
      *
@@ -218,6 +225,10 @@ class TableLocator implements LocatorInterface
 
         $className = $this->_getClassName($alias, $options);
         if ($className) {
+            if (isset($this->initializing[$className])) {
+                throw new RuntimeException('Infinite recursion. Cannot call get() on Table that is being constructed.');
+            }
+            $this->initializing[$className] = true;
             $options['className'] = $className;
         } else {
             if (empty($options['className'])) {
@@ -252,6 +263,9 @@ class TableLocator implements LocatorInterface
             $this->_fallbacked[$alias] = $this->_instances[$alias];
         }
 
+        // clear initializing flag after construction complete
+        unset($this->initializing[$options['className']]);
+
         return $this->_instances[$alias];
     }
 
@@ -315,6 +329,7 @@ class TableLocator implements LocatorInterface
     public function clear()
     {
         $this->_instances = [];
+        $this->initializing = [];
         $this->_config = [];
         $this->_fallbacked = [];
         $this->_options = [];

+ 27 - 0
tests/TestCase/ORM/Locator/TableLocatorTest.php

@@ -20,6 +20,7 @@ use Cake\ORM\Locator\TableLocator;
 use Cake\ORM\Table;
 use Cake\TestSuite\TestCase;
 use Cake\Validation\Validator;
+use RuntimeException;
 use TestApp\Infrastructure\Table\AddressesTable;
 use TestApp\Model\Table\ArticlesTable;
 use TestPlugin\Infrastructure\Table\AddressesTable as PluginAddressesTable;
@@ -422,6 +423,32 @@ class TableLocatorTest extends TestCase
     }
 
     /**
+     * Tests calling get() on a table that's being initialized.
+     *
+     * @return void
+     */
+    public function testGetInfiniteRecursion()
+    {
+        $locator = new TableLocator(['Model/Table']);
+
+        $this->expectException(RuntimeException::class);
+        $this->expectExceptionMessage('Infinite recursion.');
+        $locator->get('Recursion', ['locator' => $locator]);
+    }
+
+    /**
+     * Tests calling get() after table initialization
+     *
+     * @return void
+     */
+    public function testGetAfterGet()
+    {
+        $locator = new TableLocator(['Model/Table']);
+        $this->assertInstanceOf(ArticlesTable::class, $locator->get('Articles'));
+        $this->assertInstanceOf(ArticlesTable::class, $locator->get('Articles'));
+    }
+
+    /**
      * Tests that table options can be pre-configured for the factory method
      *
      * @return void

+ 29 - 0
tests/test_app/TestApp/Model/Table/RecursionTable.php

@@ -0,0 +1,29 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ * @since         3.9.7
+ * @license       https://opensource.org/licenses/mit-license.php MIT License
+ */
+namespace TestApp\Model\Table;
+
+use Cake\ORM\Table;
+
+/**
+ * Table with recursive reference
+ */
+class RecursionTable extends Table
+{
+    /**
+     * @inheritDoc
+     */
+    public function initialize(array $config)
+    {
+        $config['locator']->get('Recursion');
+    }
+}